`

同步/异步与阻塞/非阻塞的区别--多线程编程基础

 
阅读更多

在进行网络编程时,我们常常见到同步、异步、阻塞和非阻塞四种调用方式。这些方式彼此概念并不好理解。下面是我对这些术语的理解。

同步


  所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。按照这个定义,其实绝大多数函数都是同步调用(例如sin, isdigit等)。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。最常见的例子就是 SendMessage。该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。当对方处理完毕以后,该函数才把消息处理函数所返回的 LRESULT值返回给调用者。


异步


   异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。以CAsycSocket类为例(注意,CSocket从CAsyncSocket派生,但是起功能已经由异步转化为同步),当一个客户端通过调用 Connect函数发出一个连接请求后,调用者线程立刻可以朝下运行。当连接真正建立起来以后,socket底层会发送一个消息通知该对象。
这里提到执行部件和调用者通过三种途径返回结果:状态、通知和回调。可以使用哪一种依赖于执行部件的实现,除非执行部件提供多种选择,否则不受调用者控制。如果执行部件用状态来通知,那么调用者就需要每隔一定时间检查一次,效率就很低(有些初学多线程编程的人,总喜欢用一个循环去检查某个变量的值,这其实是一种很严重的错误)。如果是使用通知的方式,效率则很高,因为执行部件几乎不需要做额外的操作。至于回调函数,其实和通知没太多区别。


阻塞


   阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。
有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。例如,我们在CSocket中调用Receive函数,如果缓冲区中没有数据,这个函数就会一直等待,直到有数据才返回。而此时,当前线程还会继续处理各种各样的消息。如果主窗口和调用函数在同一个线程中,除非你在特殊的界面操作函数中调用,其实主界面还是应该可以刷新。
socket接收数据的另外一个函数recv则是一个阻塞调用的例子。当socket工作在阻塞模式的时候,如果没有数据的情况下调用该函数,则当前线程就会被挂起,直到有数据为止。


非阻塞


   非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。


对象的阻塞模式和阻塞函数调用


   对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性,但是并不是一一对应的。阻塞对象上可以有非阻塞的调用方式,我们可以通过一定的API去轮询状态,在适当的时候调用阻塞函数,就可以避免阻塞。而对于非阻塞对象,调用特殊的函数也可以进入阻塞调用。函数select就是这样的一个例子。

 

首先来解释同步和异步的概念,这两个概念与消息的通知机制有关.

举个例子,比如我去银行办理业务,可能选择排队等候,也可能取一个小纸条上面有我的号码,等到排到我这一号时由柜台的人通知我轮到我去办理业务了.
前者(排队等候)就是同步等待消息,而后者(等待别人通知)就是异步等待消息.在异步消息处理中,等待消息者(在这个例子中就是等待办理业务的人)往往注册一个回调机制,在所等待的事件被触发时由触发机制(在这里是柜台的人)通过某种机制(在这里是写在小纸条上的号码)找到等待该事件的人.
而在实际的程序中,同步消息处理就好比简单的read/write操作,它们需要等待这两个操作成功才能返回;而异步处理机制就是类似于select/poll之类的多路复用IO操作,当所关注的消息被触发时,由消息触发机制通知触发对消息的处理.

其次再来解释一下阻塞和非阻塞,这两个概念与程序等待消息(无所谓同步或者异步)时的状态有关.
继续上面的那个例子,不论是排队还是使用号码等待通知,如果在这个等待的过程中,等待者除了等待消息之外不能做其它的事情,那么该机制就是阻塞的,表现在程序中,也就是该程序一直阻塞在该函数调用处不能继续往下执行.相反,有的人喜欢在银行办理这些业务的时候一边打打电话发发短信一边等待,这样的状态就是非阻塞的,因为他(等待者)没有阻塞在这个消息通知上,而是一边做自己的事情一边等待.但是需要注意了,第一种同步非阻塞形式实际上是效率低下的,想象一下你一边打着电话一边还需要抬头看到底队伍排到你了没有,如果把打电话和观察排队的位置看成是程序的两个操作的话,这个程序需要在这两种不同的行为之间来回的切换,效率可想而知是低下的;而后者,异步非阻塞形式却没有这样的问题,因为打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,程序没有在两种不同的操作中来回切换.

很多人会把同步和阻塞混淆,我想是因为很多时候同步操作会以阻塞的形式表现出来,比如很多人会写阻塞的read/write操作,但是别忘了可以对fd设置O_NONBLOCK标志位,这样就可以将同步操作变成非阻塞的了;同样的,很多人也会把异步和非阻塞混淆,因为异步操作一般都不会在真正的IO操作处被阻塞,比如如果用select函数,当select返回可读时再去read一般都不会被阻塞,就好比当你的号码排到时一般都是在你之前已经没有人了,所以你再去柜台办理业务就不会被阻塞.

可见,同步/异步与阻塞/非阻塞是两组不同的概念,它们可以共存组合,也可以参见这里:
http://www.ibm.com/developerworks/cn/linux/l-async/
同步和异步:上面提到过,同步和异步仅仅是关于所关注的消息如何通知的机制,而不是处理消息的机制.也就是说,同步的情况下,是由处理消息者自己去等待消息是否被触发,而异步的情况下是由触发机制来通知处理消息者,所以在异步机制中,处理消息者和触发机制之间就需要一个连接的桥梁,在我们举的例子中这个桥梁就是小纸条上面的号码,而在select/poll等IO多路复用机制中就是fd,当消息被触发时,触发机制通过fd找到处理该fd的处理函数.

请注意理解消息通知和处理消息这两个概念,这是理解这个问题的关键所在.还是回到上面的例子,轮到你办理业务这个就是你关注的消息,而去办理业务就是对这个消息的处理,两者是有区别的.而在真实的IO操作时,所关注的消息就是该fd是否可读写,而对消息的处理就是对这个fd进行读写.同步/异步仅仅关注的是如何通知消息,它们对如何处理消息并不关心,好比说,银行的人仅仅通知你轮到你办理业务了,而如何办理业务他们是不知道的.

而很多人之所以把同步和阻塞混淆,我想也是因为没有区分这两个概念,比如阻塞的read/write操作中,其实是把消息通知和处理消息结合在了一起,在这里所关注的消息就是fd是否可读/写,而处理消息则是对fd读/写.当我们将这个fd设置为非阻塞的时候,read/write操作就不会在等待消息通知这里阻塞,如果fd不可读/写则操作立即返回.

很多人又会问了,异步操作不会是阻塞的吧?已经通知了有消息可以处理了就一定不是阻塞的了吧?
其实异步操作是可以被阻塞住的,只不过通常不是在处理消息时阻塞,而是在等待消息被触发时被阻塞.比如select函数,假如传入的最后一个timeout参数为NULL,那么如果所关注的事件没有一个被触发,程序就会一直阻塞在这个select调用处.而如果使用异步非阻塞的情况,比如aio_*组的操作,当我发起一个aio_read操作时,函数会马上返回不会被阻塞,当所关注的事件被触发时会调用之前注册的回调函数进行处理,具体可以参见我上面的连接给出的那篇文章.回到上面的例子中,如果在银行等待办理业务的人采用的是异步的方式去等待消息被触发,也就是领了一张小纸条,假如在这段时间里他不能离开银行做其它的事情,那么很显然,这个人被阻塞在了这个等待的操作上面;但是呢,这个人突然发觉自己烟瘾犯了,需要出去抽根烟,于是他告诉大堂经理说,排到我这个号码的时候麻烦到外面通知我一下(注册一个回调函数),那么他就没有被阻塞在这个等待的操作上面,自然这个就是异步+非阻塞的方式了.
分享到:
评论
1 楼 brucehandy 2015-05-13  
谢谢,部分理解了

相关推荐

    socket 多线程 例程 非阻塞模式

    同时,多线程编程需要注意同步问题,例如使用互斥量(mutex)或事件(event)来防止线程间的资源竞争。 这个示例中的源代码包含了客户端和服务器端的实现,通过运行这些代码,你可以实际观察到多线程非阻塞模式下的...

    线程同步与异步套接字编程

    在计算机编程领域,尤其是网络编程中,线程同步与异步套接字编程是至关重要的概念,它们直接影响到程序的性能、稳定性和可扩展性。本文将深入探讨这两个主题,并结合实际应用进行详细阐述。 首先,我们需要理解线程...

    C#多线程与异步的区别

    - **编程简单**:线程中的代码仍然是顺序执行的,这使得多线程编程更加直观和易于理解。 - **灵活性高**:多线程可以在不同类型的计算密集型任务中使用,如图形处理、算法计算等。 **缺点**: - **上下文切换开销**...

    C#多线程编程源码奉上

    在这个“C#多线程编程源码”中,我们可以深入学习如何在Visual Studio 2008环境下利用C#进行多线程操作,包括UI线程和异步调用的实现。 1. **多线程基础** - **线程创建**:C#中可以使用`System.Threading.Thread`...

    L6多线程&异步编程

    "多线程编程技术基础.ppt"可能详细介绍了这些内容,并提供了避免竞态条件和死锁的策略。例如,Java的`synchronized`关键字用于同步访问,Python的`Lock`对象用于锁定资源,C#的`Monitor`类实现线程同步。 接着,...

    计算机网络课程设计----多线程Web服务器

    4. **I/O模型**:多线程Web服务器可能会使用不同的I/O模型,如阻塞I/O、非阻塞I/O、IO复用(如select、poll、epoll)、信号驱动I/O或异步非阻塞I/O。选择合适的I/O模型对于性能优化至关重要。 5. **错误处理与日志...

    visual c++ MFC之Lesson16线程同步与异步套接字编程

    线程同步是多线程编程中一个重要的方面,它确保了在共享资源时避免数据冲突和死锁。异步套接字编程则是Windows环境下网络通信的一种高效方法。 线程同步: 1. 事件对象:事件对象是Windows内核对象的一种,它有一个...

    ASP.NET多线程编程

    ASP.NET多线程编程是开发高效、响应迅速的Web应用程序的关键技术之一。它允许开发者在同一个应用程序中同时执行多个任务,提升系统性能并优化资源利用。在ASP.NET中,多线程可以用于后台处理、异步操作、长时间运行...

    Twisted与异步编程入门

    虽然从理论上能够提高程序的并发能力,但实际上多线程编程较为复杂,需要解决线程间通信等问题。 - 特点:任务在独立的线程中运行,操作系统负责调度。 - 图解说明:通过图表展示了任务如何被分配到不同线程中...

    cpp-netskeleton异步非阻塞多协议网络的CC库

    《cpp-netskeleton:探索异步非阻塞多协议网络编程的C++库》 在IT行业中,网络编程是至关重要的部分,它涉及到系统间的数据通信和交互。cpp-netskeleton是一个专注于异步非阻塞多协议网络的C/C++库,它为开发者提供...

    大恒-双相机开发-C#-多线程-项目开源

    对于想要深入学习C#图像处理和多线程编程的开发者来说,这是一个非常有价值的开源项目。通过研究这个项目,我们可以了解到如何在实际应用中有效地结合各种技术,解决复杂的问题,提升系统的性能。

    基于VC++的简单同步-异步通信系统

    1. **线程管理**:VC++支持多线程编程,通过创建线程对象(如CreateThread函数),可以在不同的线程上下文中执行同步或异步任务。 2. **事件对象**:如Event、Mutex和Semaphore,这些是同步原语,用于线程间的同步...

    CSOCKET同步异步通讯与多线程总结[定义].pdf

    ### CSOCKET同步异步通讯与多线程总结 #### 一、引言 在现代软件开发尤其是网络应用领域,Socket编程技术是实现不同计算机间通信的基础。本文将深入探讨C#环境下Socket同步与异步通信的基本概念、核心原理以及如何...

    网络编程和多线程网络编程和多线程

    2. **非阻塞I/O**:多线程配合非阻塞I/O模型,可以提高服务器的性能,如使用select、poll或epoll等系统调用监控多个套接字的读写状态。 3. **任务分解**:复杂的网络任务可以分解为多个子任务,由不同线程并行处理...

    Linux多线程编程手册,linux多线程编程手册pdf,C,C++

    总之,Linux多线程编程手册深入浅出地介绍了在Linux系统中使用C++和C进行多线程网络编程的相关知识,从基础到高级,涵盖了线程创建、同步、通信、网络编程等多个方面,是学习和实践Linux多线程编程的重要参考资料。

    C++面向对象多线程编程-pdf

    5. 多线程编程挑战与实践: - 死锁:多个线程互相等待对方释放资源,导致僵局。 - 活锁:线程不断尝试执行无法成功的行为,而非阻塞。 - 数据竞争:多个线程并发访问共享资源,可能导致不一致的结果。 - 线程...

    C#Socket同步,异步编程实例.rar

    Socket编程在IT领域中是网络通信的基础,尤其是在C#中,它提供了丰富的API来实现同步和异步的网络通信。本实例将详细讲解C#中Socket的同步与异步编程模型,帮助你深入理解这两种模式的工作原理以及如何在实际项目中...

    《Linux多线程服务端编程:使用muduo C++网络库》.(陈硕).[PDF]

    1. **Linux网络编程基础**:首先,书中会介绍Linux下的网络编程基础知识,包括套接字API,TCP/IP协议栈的工作原理,以及I/O模型,如同步I/O、异步I/O和多路复用I/O(如select、poll、epoll)等。这些是构建服务端...

    lettuce-core,高级java redis客户端,用于线程安全同步、异步和响应性使用。支持群集、哨兵、管道和编解码器。.zip

    异步模式则利用非阻塞I/O提高并发性能,尤其在高并发环境下表现优异;响应式编程模式则允许开发者以声明式方式处理数据流,极大地简化了代码并提高了系统的可维护性。 Lettuce-core不仅提供了全面的Redis命令支持,...

Global site tag (gtag.js) - Google Analytics