当前位置:C++技术网 > 资讯 > IO完成端口分析:2 出现并使用广泛的原因分析

IO完成端口分析:2 出现并使用广泛的原因分析

更新时间:2015-06-25 00:00:26浏览次数:1+次

    在《IO完成端口分析:解惑1--基本概念辨析》文中已经介绍了IO完成端口的基本概念,没看的先看看吧。
    IO完成端口是一个非常不错的东西,所以,好在哪里,我们需要了解。不然,我们也没法领略到它的精华。先看到它的好处,最后再跟大家讲讲IO完成端口的运作机制。当然本篇只是简要的讲述运作机制,大家从整体上对它有个把握,然后才好进一步深入,否则只会越来越困惑。
    IO完成端口应用在服务器程序中,用来处理大量的用户请求。所以,也是应用在网络开发中。在网络开发中,多数是通过异步执行的。这里简单说一下同步和异步的概念。同步异步的主体是线程,同步指的是这个线程的执行方式。如果说,线程发出调用,这个调用会耗时比较长,通常IO就是典型的代表。如果线程等待调用执行完成然后继续向下执行,那么这个线程的执行顺序就是线性的,连续的。这样的线性连续的执行就是同步。而多个线程的同步,其实也是维持一个操作的线性执行的过程。所以,可以将同步理解为线性执行任务的过程。只不过,同步会带来等待,等待就会带来性能损失。何谓损失,就是CPU不能时刻执行有效的任务。在那里空等就是浪费性能。当然,有一种办法就是挂起线程,让其他线程运行。只不过,这个时候,你的线程不能执行,你的程序性能不好而已。对于程序来说,说性能问题,当然指你的程序的性能。所以,为了提高性能,就产生了异步的概念。这个异步就让线程执行某个任务不是线性的了。当线程请求一个IO读取数据,读完数据后,才能对数据分析。但是数据没来,也不能空等着,然后就去执行其他一些操作,这样也弥补了性能损失,实际上还是要等,只不过这种等不是什么事也不做的等,期间可以做一些其他不想关的准备工作,或者其他的,看你怎么安排。最后,设备执行完IO,会通知线程,然后线程继续处理这个数据。这样就让编程变得麻烦。而设备怎么通知线程呢?这是个问题。通常的做法就是使用回调函数,windows中叫做APC(异步过程调用)。APC就是回调函数和异步操作实现的一个机制,其实也没什么大不了的,并没有什么高大上的,只不过是技术的组合使用而已。这样,线程提交IO请求时,就需要提供一个回调函数,将回调函数地址同时也传给IO设备,IO设备完成时,就将执行的结果发回到回调函数。这个回调函数就是请求IO的线程提供的,这个线程如果之前准备工作也做完了,还是没有数据到来,就没办法,只有等待了。当这个IO执行完,系统就会通知线程数据到来了,然后线程就去执行回调函数处理数据。如果线程处于挂起等待状态,那么线程就被唤醒。如果线程正在执行其他操作,且不可中断,只有等这个不可中断的执行完,再切过来处理数据,这样会延迟数据的处理。当然这是不可避免的。
    上面说的APC有个专业的名词叫做可提醒IO。我就这样的简单描述一下,让大家更好理解,而不是去说些名词,让大家更加负担重重。通过这个过程,大家也清楚APC的机制。可提醒IO,大家稍微查阅一下资料,也就一目了然了。
    上面提到的,每个线程都会发出IO请求,并且也要提供回调函数。要提供回调函数,让编程变得麻烦。这个还是小问题。真正的问题就是性能问题。因为每个线程的回调函数都是每个线程自己执行的。如果这个线程发出了好多IO,那么这个线程就有的忙了。假如其他线程并没有事情,假如是2个CPU,这个线程只占用一个CPU,那么另外一个CPU就是空闲的,非常的浪费。其他线程也只能看,帮不上忙。这样就是典型的“负载不均衡”。因为一个CPU忙的要死,另一个CPU闲的要死,这是很不均衡的。在服务器程序中,性能是非常重要的,所以微软就为了解决这个问题,提出了IO完成端口。
    说了这么多,我并不是给大家罗列好处,而是用一个很自然的逻辑给大家讲述,相信这样更好理解IO完成端口的好处。
    当然,IO完成端口还克服了一个问题。服务器的程序当然要使用并发编程模型来实现,因为这样性能很好。当然,大家可能有个误解,以为并发的线程越多性能越高。其实并不是的。通常TCP处理用户请求,接受一个请求,就创建一个线程来处理。这样如果处理速度比请求速度慢,这样就会集聚大量的线程。然而,由于CPU的数量是有限的,一个CPU执行一个线程,也就是说,其他线程就在等待,等待执行。此时的线程都是等待状态,并没有挂起,都在等待调度执行。并发就是一段时间内都在执行的意思,这样,线程的执行就要大量的切换,线程越多,切换就越多。切换的越多,每个线程真正执行的时间并不多,这样就把大把的时间浪费在线程切换了。线程相当于“屁股还没有坐热就要从CPU中出来了”。性能其实降低了。这一点务必要分清楚。
    IO完成端口使用前就初始化了一个线程池,这个线程池的所有线程都执行同一个线程函数,而不是回调函数。这个就相当于你创建线程时设置的线程函数一样,这样显得自然。并且线程池的所有线程都会执行这个线程函数来处理,所以编程就简单了。同时,线程池是有系统来维护的,我们只要使用即可,所以也降低了我们的使用成本。
    IO完成端口就集中了异步、负载均衡、并发优化、简化编程、降低用户程序维护成本的优点于一体,得到了大家的认可。所以也是使用非常广泛的。