同步函数与异步函数

依据微软的MSDN上的解说:编程

(1)   同步函数:当一个函数是同步执行时,那么当该函数被调用时不会当即返回,直到该函数所要作的事情全都作完了才返回。设计模式

(2)   异步函数:若是一个异步函数被调用时,该函数会当即返回尽管该函数规定的操做任务尚未完成。安全

(3) 在一个线程中分别调用上述两种函数会对调用线程有何影响呢?服务器

        当一个线程调用一个同步函数时(例如:该函数用于完成写文件任务),若是该函数没有当即完成规定的操做,则该操做会致使该调用线程的挂起(将CPU的使用权交给系统,让系统分配给其余线程使用),直到该同步函数规定的操做完成才返回,最终才能致使该调用线程被从新调度。网络

         当一个线程调用的是一个异步函数(例如:该函数用于完成写文件任务),该函数会当即返回尽管其规定的任务尚未完成,这样线程就会执行异步函数的下一条语句,而不会被挂起。那么该异步函数所规定的工做是如何被完成的呢?固然是经过另一个线程完成的了啊;那么新的线程是哪里来的呢?多是在异步函数中新建立的一个线程也多是系统中已经准备好的线程。多线程

(4)一个调用了异步函数的线程如何与异步函数的执行结果同步呢?架构

        为了解决该问题,调用线程须要使用“等待函数”来肯定该异步函数什么时候完成了规定的任务。所以在线程调用异步函数以后当即调用一个“等待函数”挂起调用线程,一直等到异步函数执行完其全部的操做以后,再执行线程中的下一条指令。app

咱们是否已经发现了一个有趣的地方呢?!就是咱们可使用等待函数将一个异步执行的函数封装成一个同步函数。异步

2.同步调用与异步调用socket

        操做系统发展到今天已经十分精巧,线程就是其中一个杰做。操做系统把 CPU 处理时间划分红许多短暂时间片,在时间 T1 执行一个线程的指令,到时间 T2 又执行下一线程的指令,各线程轮流执行,结果好象是全部线程在并肩前进。这样,编程时能够建立多个线程,在同一期间执行,各线程能够“并行”完成不一样的任务。
        在单线程方式下,计算机是一台严格意义上的冯·诺依曼式机器,一段代码调用另外一段代码时,只能采用同步调用,必须等待这段代码执行完返回结果后,调用方才能继续往下执行。有了多线程的支持,能够采用异步调用,调用方和被调方能够属于两个不一样的线程,调用方启动被调方线程后,不等对方返回结果就继续执行后续代码。被调方执行完毕后,经过某种手段通知调用方:结果已经出来,请酌情处理。

   计算机中有些处理比较耗时。调用这种处理代码时,调用方若是站在那里苦苦等待,会严重影响程序性能。例如,某个程序启动后若是须要打开文件读出其中的数据,再根据这些数据进行一系列初始化处理,程序主窗口将迟迟不能显示,让用户感到这个程序怎么等半天也不出来,太差劲了。借助异步调用能够把问题轻松化解:把整个初始化处理放进一个单独线程,主线程启动此线程后接着往下走,让主窗口瞬间显示出来。等用户盯着窗口犯呆时,初始化处理就在背后悄悄完成了。程序开始稳定运行之后,还能够继续使用这种技巧改善人机交互的瞬时反应。用户点击鼠标时,所激发的操做若是较费时,再点击鼠标将不会当即反应,整个程序显得很沉重。借助异步调用处理费时的操做,让主线程随时恭候下一条消息,用户点击鼠标时感到轻松快捷,确定会对软件产生好感。
        异步调用用来处理从外部输入的数据特别有效。假如计算机须要从一台低速设备索取数据,而后是一段冗长的数据处理过程,采用同步调用显然很不合算:计算机先向外部设备发出请求,而后等待数据输入;而外部设备向计算机发送数据后,也要等待计算机完成数据处理后再发出下一条数据请求。双方都有一段等待期,拉长了整个处理过程。其实,计算机能够在处理数据以前先发出下一条数据请求,而后当即去处理数据。若是数据处理比数据采集快,要等待的只有计算机,外部设备能够连续不停地采集数据。若是计算机同时链接多台输入设备,能够轮流向各台设备发出数据请求,并随时处理每台设备发来的数据,整个系统能够保持连续高速运转。编程的关键是把数据索取代码和数据处理代码分别归属两个不一样的线程。数据处理代码调用一个数据请求异步函数,而后径自处理手头的数据。待下一组数据到来后,数据处理线程将收到通知,结束 wait 状态,发出下一条数据请求,而后继续处理数据。
        异步调用时,调用方不等被调方返回结果就转身离去,所以必须有一种机制让被调方有告终果时能通知调用方。在同一进程中有不少手段能够利用,笔者经常使用的手段是回调、event 对象和消息。
        回调:
回调方式很简单:调用异步函数时在参数中放入一个函数地址,异步函数保存此地址,待有告终果后回调此函数即可以向调用方发出通知。若是把异步函数包装进一个对象中,能够用事件取代回调函数地址,经过事件处理例程向调用方发通知。

  event : event 是 Windows 系统提供的一个经常使用同步对象,以在异步处理中对齐不一样线程之间的步点。若是调用方暂时无事可作,能够调用 wait 函数等在那里,此时 event 处于 nonsignaled 状态。当被调方出来结果以后,把 event 对象置于 signaled 状态,wait 函数便自动结束等待,使调用方从新动做起来,从被调方取出处理结果。这种方式比回调方式要复杂一些,速度也相对较慢,但有很大的灵活性,能够搞出不少花样以适应比较复杂的处理系统。

        消息:借助 Windows 消息发通知是个不错的选择,既简单又安全。程序中定义一个用户消息,并由调用方准备好消息处理例程。被调方出来结果以后当即向调用方发送此消息,并经过 WParam 和 LParam 这两个参数传送结果。消息老是与窗口 handle 关联,所以调用方必须借助一个窗口才能接收消息,这是其不方便之处。另外,经过消息联络会影响速度,须要高速处理时回调方式更有优点。
        若是调用方和被调方分属两个不一样的进程,因为内存空间的隔阂,通常是采用 Windows 消息发通知比较简单可靠,被调方能够借助消息自己向调用方传送数据。event 对象也能够经过名称在不一样进程间共享,但只能发通知,自己没法传送数据,须要借助 Windows 消息和 FileMapping 等内存共享手段或借助 MailSlot 和 Pipe 等通讯手段。
        异步调用原理并不复杂,但实际使用时容易出莫名其妙的问题,特别是不一样线程共享代码或共享数据时容易出问题,编程时须要时时注意是否存在这样的共享,并经过各类状态标志避免冲突。Windows 系统提供的 mutex 对象用在这里特别方便。mutex 同一时刻只能有一个管辖者。一个线程放弃管辖权后,另外一线程才能接管。当某线程执行到敏感区以前先接管 mutex,使其余线程被 wait 函数堵在身后;脱离敏感区以后当即放弃管辖权,使 wait 函数结束等待,另外一个线程便有机会光临此敏感区。这样就能够有效避免多个线程进入同一敏感区。
        因为异步调用容易出问题,要设计一个安全高效的编程方案须要比较多的设计经验,因此最好不要滥用异步调用。同步调用毕竟让人更舒服些:无论程序走到哪里,只要死盯着移动点就能心中有数,不至于象异步调用那样,总有一种四面受敌、惶惶不安的感受。必要时甚至能够把异步函数转换为同步函数。方法很简单:调用异步函数后立刻调用 wait 函数等在那里,待异步函数返回结果后再继续往下走。

假如回调函数中包含文件处理之类的低速处理,调用方等不得,须要把同步调用改成异步调用,去启动一个单独的线程,而后立刻执行后续代码,其他的事让线程慢慢去作。一个替代办法是借 API 函数 PostMessage 发送一个异步消息,而后当即执行后续代码。这要比本身搞个线程省事许多,并且更安全。

 

若是你的服务端的客户端数量多,你的服务端就采用异步的,可是你的客户端能够用同步的,客户端通常功能比较单一,收到数据后才能执行下面的工做,因此弄成同步的在那等。

1、举个打电话的例子:

阻塞 block 是指,你拨通某人的电话,可是此人不在,因而你拿着电话等他回来,其间不能再用电话。同步大概和阻塞差很少。

非阻塞 nonblock 是指,你拨通某人的电话,可是此人不在,因而你挂断电话,待会儿再打。至于到时候他回来没有,只有打了电话才知道。即所谓的“轮询 / poll”。

异步是指,你拨通某人的电话,可是此人不在,因而你叫接电话的人告诉那人(leave a message),回来后给你打电话(call back)。

2、同步异步与阻塞和非阻塞是两种不一样的概念来着

同步异步指的是通讯模式,而阻塞和非阻塞指的是在接收和发送时是否等待动做完成才返回

首先是通讯的同步主要是指客户端在发送请求后,必须得在服务端有回应后才发送下一个请求。因此这个时候的全部请求将会在服务端获得同步

其次是通讯的异步指客户端在发送请求后,没必要等待服务端的回应就能够发送下一个请求,这样对于全部的请求动做来讲将会在服务端获得异步,这条请求的链路就象是一个请求队列,全部的动做在这里不会获得同步的。

阻塞和非阻塞只是应用在请求的读取和发送。

在实现过程当中,若是服务端是异步的话,客户端也是异步的话,通讯效率会很高,但若是服务端在请求的返回时也是返回给请求的链路时,客户端是能够同步的,这种状况下,服务端是兼容同步和异步的。相反,若是客户端是异步而服务端是同步的也不会有问题,只是处理效率低了些。

 

设想你是一位体育老师,须要测验100位同窗的400米成绩。你固然不会让100位同窗一块儿起跑,由于当同窗们返回终点时,你根原本不及掐表记录各位同窗的成绩。

若是你每次让一位同窗起跑并等待他回到终点你记下成绩后再让下一位起跑,直到全部同窗都跑完。恭喜你,你已经掌握了同步阻塞模式。你设计了一个函数,传入参数是学生号和起跑时间,返回值是到达终点的时间。你调用该函数100次,就能完成此次测验任务。这个函数是同步的,由于只要你调用它,就能获得结果;这个函数也是阻塞的,由于你一旦调用它,就必须等待,直到它给你结果,不能去干其余事情。

若是你一边每隔10秒让一位同窗起跑,直到全部同窗出发完毕;另外一边每有一个同窗回到终点就记录成绩,直到全部同窗都跑完。恭喜你,你已经掌握了异步非阻塞模式。你设计了两个函数,其中一个函数记录起跑时间和学生号,该函数你会主动调用100次;另外一个函数记录到达时间和学生号,该函数是一个事件驱动的callback函数,当有同窗到达终点时,你会被动调用。你主动调用的函数是异步的,由于你调用它,它并不会告诉你结果;这个函数也是非阻塞的,由于你一旦调用它,它就立刻返回,你不用等待就能够再次调用它。但仅仅将这个函数调用100次,你并无完成你的测验任务,你还须要被动等待调用另外一个函数100次。

固然,你立刻就会意识到,同步阻塞模式的效率明显低于异步非阻塞模式。那么,谁还会使用同步阻塞模式呢?不错,异步模式效率高,但更麻烦,你一边要记录起跑同窗的数据,一边要记录到达同窗的数据,并且同窗们回到终点的次序与起跑的次序并不相同,因此你还要不停地在你的成绩册上查找学生号。忙乱之中你每每会张冠李戴。你可能会想出更聪明的办法:你带了不少块秒表,让同窗们分组互相测验。恭喜你!你已经掌握了多线程同步模式!

每一个拿秒表的同窗均可以独立调用你的同步函数,这样既不容易出错,效率也大大提升,只要秒表足够多,同步的效率也能达到甚至超过异步。

能够理解,你现的问题多是:既然多线程同步既快又好,异步模式还有存在的必要吗?

很遗憾,异步模式依然很是重要,由于在不少状况下,你拿不出不少秒表。你须要通讯的对端系统可能只容许你创建一个SOCKET链接,不少金融、电信行业的大型业务系统都如此要求。

1、同步阻塞模式

在这个模式中,用户空间的应用程序执行一个系统调用,并阻塞,直到系统调用完成为止(数据传输完成或发生错误)。

2、同步非阻塞模式
同步阻塞 I/O 的一种效率稍低的。非阻塞的实现是 I/O 命令可能并不会当即知足,须要应用程序调用许屡次来等待操做完成。这可能效率不高,由于在不少状况下,当内核执行这个命令时,应用程序必需要进行忙碌等待,直到数据可用为止,或者试图执行其余工做。由于数据在内核中变为可用到用户调用 read 返回数据之间存在必定的间隔,这会致使总体数据吞吐量的下降。但异步非阻塞因为是多线程,效率仍是高。

/* create the connection by socket * means that connect "sockfd" to "server_addr" * 同步阻塞模式 */
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { perror("connect"); exit(1); } /* 同步非阻塞模式 */
while (send(sockfd, snd_buf, sizeof(snd_buf), MSG_DONTWAIT) == -1) { sleep(1); printf("sleep\n"); }

 

网络程序开发流程:

1. 需求分析

2. 根据需求, 进行数据包设计(通常分为包头和包数据两部分, 包头用来存储包的必要信息, 如信息类型, 数据长度等)

3. 定义传输协议(如何传输).

4. 理解需求, 设计整体架构, 利用设计模式等方法, 进行问题分析和设计类图.

5. 实现, 一般要配合多线程来实现通讯问题. (通常有等待客户请求线程, 接收数据线程, 发送数据线程, 资源清理线程).

6. 实现服务器端.

7. 实现客户端.

相关文章
相关标签/搜索