首页 > 编程知识 正文

python线程间通信,java线程通信的几种方式

时间:2023-05-05 03:15:50 阅读:61998 作者:1515

为什么通信是人的基本需要。 过程作为人的发明,自然离不开人的习性,也有通信需求。如果进程之间不进行任何通信,那么进程所能完成的任务就要大打折扣。这需要父子进程之间的通信。

线程之间的通信需要更多。 由于一个进程通常包含多个线程,因此这些线程之间存在用于资源共享的伙伴关系。 这种合作关系可以表达为相互独立,但往往是相互作用的。 这就是通信。 就像舞台上的多个演员一样,他们之间有合作关系,共同把戏演得很好。 这些演员在舞台上可以扮演各自的角色,但不说话,没有肢体接触,也就是说不互动,但往往会进行白色和拥抱等互动操作。

线程之间的交互我们就称之为线程通信。线程通信是从进程通信演变而来的,进程通信有个专有缩写,叫IPC(Inter-Process Communication)。由于每个进程至少有一个线程,进程的通信就是进程里面的线程通信在随后的讨论中,统一使用线程通信进行说明。

线程之间的通信是如何进行的呢?

舞台上的演员可以通过白色、手势、拥抱等方式进行交互通信。 同样,线程也可以进行同样的通信。 让我们来看看线程的这些交互。

管道、签名管道和插座演员最常用的对话手段是白色。 白色是指一方发出声音,另一方接受声音。 声音的传递通过空气(面对面或无线对话)、电缆)、有线电话)进行。 同样,线程白色为一个线程发出某种数据信息,另外一方接受数据信息,这些数据信息通过一片共享的存储空间进行传递。

这样,一个线程向该存储空间的一端写入信息,而另一个线程从存储空间的另一端读取信息。 这看起来像什么? 管子。 管道占用的空间可以是内存也可以是磁盘。 就像两个人可以把白色的介质做成空气,也可以做成电缆一样。 要创建管道,请访问一个线程只需调用管道创建的系统调用即可。

管道(无名管道)基本上是线性字节数组,类似于文件。 通过读写文件进行访问,但不是文件。 因为通过文件系统看不到管道的存在。 此外,如上所述,管道可以安装在内存中,但文件很少安装在内存中。 创建管道在shell命令行和程序中是不同的。 在shell命令行中,只需使用符号“|”。

在程序中,必须使用系统调用popen (或pipe )创建管道。 popen必须将目标进程指定为参数,并在调用函数的进程和给定的目标进程之间创建管道。 这类似于人们打电话时必须提供对方的号码才能建立连接。

创建时,还必须指定指示管道类型的参数。 导线管或光管。 另一方面,pipe调用返回两个文件描述符。 文件描述符与句柄不同,它是标识文件流的整数。 一个用于从管道读取操作,另一个用于写入管道。 也就是说,pipe连接两个文件描述符,使其一端可读,另一端可写。 通常,在使用pipe调用创建管道后,使用fork生成两个进程。 这两个进程使用pipe返回的两个文件描述符进行通信。

例如,以下代码段创建管道,并使用它在父子进程之间进行通信: int pp [2 ];

语言

int fd[2]; //用于存储返回管道的描述符pipe(FD );//软盘[0]读,软盘[1]写int pid=fork (); //assert(PID!=-1; if(PID==0)……………………………………) http://www.Sina.com/例如,要使用popen,必须提供另一个进程的文件名,并使用pipe

如果签名管道位于两个无关线程上,则为(无名)管道的一个重要特点是使用管道的两个线程之间必须存在某种关系,qrdxn,签名管道为命名通信管道。 签名管道与文件系统共享名称空间,在打印中可以从文件系统看到签名管道。 也就是说,签名管道的名称不能与文件系统中的任何文件名重复。

一个线程创建签名管道后,另一个线程可以使用open打开该管道,而无名管道不能使用open ),可以与另一端进行交流。 (或使用现有管道)。 记名管路的名称由两部分组成:计算机名称和管路名称,如两个不同进程里面的线程,之间进行管道通信,则需要使用记名管道

同一主机允许多个同名管道的实例,可以在不同的进程中打开,但不同的管道都有自己的管道缓冲区,并且有自己的互不影响的通信环境。 命名管道支持多个客户端连接到一个服务器端。 命名管道客户端不仅可以与本地计算机上的服务进行通信,还可以与其他主机上的服务进行通信。

管道和签名管道具有简单、无需特殊设计(指APP应用)即可与其他过程进行远程通信的优点,但其缺点也很明显。 首先,管道和记名管道并不是所有操作系统都是专用的。例如[主机名]管道[管道名]。

了。其次,管道通信需要在相关的进程间进行(无名管道),或者需要知道按名字来打开(记名管道),而这在某些时候会十分不便。

套接字

套接字(socket)是另外一种可以用于进程间通信的机制!套接字首先在BSD中出现,随后几乎渗透到所有主流操作系统。套接字的功能非常强大,可以支持不同层面,不同应用,跨网络的通信。使用套接字进行通信需要双方均创建一个套接字,其中一方作为服务器方,另外一方作为客户方。服务器方必须先创建一个服务器套接字,然后在该套接字上进行监听,等待远方的连接请求。欲与服务器通信的客户则创建一个客户套接字,然后向服务器套接字发送连接请求。服务器套接字在收到连接请求后,将在服务器机器上创建一个客户套接字,与远方的客户机上的客户套接字形成点到点的通信通道。之后,客户端和服务器端就可以通过send和recv命令在这个创建的套接字通道上进行交流了。

服务器套接字有点类似于传说中的虫洞(worm hole)。虫洞的一端是开放的,它在宇宙内或宇宙间漂移着,另外一端处于一个不同的宇宙,监听是否有任何东西从虫洞来。而欲使用虫洞者需要找到虫洞的开口端(发送连接请求),然后穿越虫洞即可。

这里需要指出的是服务器套接字既不发送数据,也不接收数据(指不接受正常的用户数据而不是连接请求数据),而仅仅是生产出“客户"套接字。 当其他(远方)的客户套接字发出一个连接请求时,我们就创建一个客户套接字。一旦客户套接字clientsocket创建成功,与客户的通信任务就交给了这个刚刚创建的客户套接字。而原本的服务器套接字serversocket则回到其原来的监听操作上。

套接字由于其功能强大而获得了很大发展,并出现了许多种类。不同的操作系统均支持或实现了某种套接字功能。例如按照传输媒介是否为本地,套接字可以分为本地(UNIX域)套接字和网域套接字。而网域套接字又按照其提供的数据传输特性分为几个大类,分别是:

数据流套接字(stream socket):揖供双向,有序、可靠、非重复数据通信。电报流套接字( datagram socket)|提供双向消息流。数据不一定按序到达。序列包套接字( sequential packet):提供双向,有序、可靠连接,包有最大限制。裸套接字(raw socket):提供对下层通信协议的访问。

套接字从某种程度上来说非常繁杂,各种操作系统对其处理并不完全一样。因此,如要了解某个特定套接字实现,读者需要查阅关于该套接字实现的具体手册或相关文档。

信号

管道和套接字虽然提供了丰富的通信语义,并且也得到了广泛应用,但它们也存在某些缺点,并且在某些时候,这两种通信机制会显得很不好使。

首先,如果使用管道和套接字方式来通信,必须事先在通信的进程间建立连接(创建管道或套接字),这需要消耗系统资源。其次,通信是自愿的。 即一方虽然可以随意往管道或套接字发送信息,对方却可以选择接收的时机。即使对方对此充耳不闻,你也奈何不得。再次,由于建立连接消耗时间,一旦建立,我们就想进行尽可能多的通信。而如果通信的信息量微小,,如我们只是想通知一个进程某件事情的发生,则用管道和套接字就有点“杀鸡用牛刀"的味道,效率十分低下。
因此,我们需要一种不同的机制来处理如下通信需求:

想迫使一方对我们的通信立即作出回应。我们不愿事先建立任何连接,面是临时突然觉得需要向某个进程通信。传输的信息量微小,使用管道或套接字不划算。

应付上述需求,我们使用的是信号( signal )。

那么信号是什么呢?在计算机里,信号就是一个内核对象,或者说一个内核数据结构。发送方将该数据结构的内容填好,并指明该信号的目标进程后,发出特定的软件中断。操作系统接受到特定的中断请求后,知道是有进程要发送信号,于是到特定的内核数据结构里查找信号接受方,并进行通知。接到通知的进程则对信号进行相应处理。如果对方选择不对信号作出反应,则将终止操作系统运行。

信号量

信号量( Semaphore)是由荷兰人E W. Dijkstra 在60年代所构思出的一种程序设计构造。其原型来源于铁路的运行:在一条单轨铁路上,任何时候只能有一列列车行驶在上面。而管理这条铁路的系统就是信号量。任何一列火车必须等到表明铁路可以行驶的信号后才能进入轨道。当一列火车进入单轨运行后,需要将信号改为禁止进人,从而防止别的火车同时进入轨道。面当火车驶出单轨后,则需要将信号变回到允许进入状态。

在计算机里,信号量实际上就是一个简单整数。一个进程在信号变为0或者1的情况下推进,并且将信号变为1或0来防止别的进程推进。当进程完成任务后,则将信号再改变为0或1,从而允许其他进程执行。

信号量不光是一种通信机制,更是一种同步机制。

共享内存

管道,套接字,信号,信号量,虽然满足了多种通信需要,但还是有一种需要未能满足。这就是两个进程需要共享大量数据。这就像两个人,他们互相喜欢,并想要一起生活时(共享大量数据),打电话,握手,对白等就显得不够了,这个时候需要的是拥抱,只有将其紧紧拥抱于怀,感觉才最到位,也才能尽可能地共享。

进程的拥抱就是共享内存。共享内存就是两个进程共同拥有同一片内存。 这片内存中的任何内容,二者均可以访问。要使用共享内存进行通信,一个进程首先创建一片内存空间专门作为通信用,而其他进程则将该片内存映射到自己的(虚拟)地址空间。这样,读写自己地址空间中对应共享内存的区域时,就是在和其他进程进行通信。

乍一看,共享内存有点像管道,有些管道不也是一片共享内存吗?这是形似而神不似。首先,使用共享内存机制通信的两个进程必须在同一台物理机器上其次,共享内存的访问方式是随机的,而不是只能从一端写,另一端读。还有一点,就是管道中的数据一读就没有了(只能读一次),而共享内存中的数据可以反复读(只要不被覆盖,删除)因此其灵活性比管道和套接字大很多,能够传递的信息也复杂得多。

共享内存的缺点是管理复杂,且两个进程必须在同一台物理机器上才能使用这种通信方式。共享内存的另外一个缺点是安全性脆弱。因为两个进程存在一片共享的内存,如果一个进程染有病毒,很容易就会传给另外一个进程。就像两个紧密接触的人,一个人的病毒是很容易传给另外一个人的。

这里需要提请读者注意的是,使用全局变量在同一个进程的线程间实现通信不称为共享内存。

消息队列

消息队列是一列具有头和尾的消息排列,新来的消息放在队列尾部,而读取消息则从队列头部开始。

乍一看,这不是管道吗?一头儿读、一头儿写?没错。这的确看上去像管道。但它不是管道。首先它无需固定的读写进程,任何进程都可以读写(当然是有权限的讲程)。其次,它可以同时支持多个进程,多个进程可以读写消息队列。即所谓的多对多,而不是管道的点对点。另外,消息队列只在内存中实现。

最后,它并不是只在UNIX和类UNIX操作系统实现。几乎所有主流操作系统都支持消息队列。

其他通信机制

除了上面介绍的主流通信方式外,有些操作系统还提供了一些其操作系统所特有的通信机制,例如Windows支持的进程通信方式就有剪贴板(clipboard), COM/DCOM,动态数据交换(DDE) ,邮箱( mailslots) ;而 solaris则有所谓的solaris 门机制,让客户通过轻量级(16KB)系统调用使用服务器的服务。

虽然进程之间的通信机制繁多,且每种机制有着自己独特的特性,但归根结底都来源于AT&T的 UNIX V系统。该系统在1983年加入了对共享内存、信号量和消息队列的支持。这三者就是众所周知的System V IPC( POSIX IPC 也是源于该系统并成为当前IPC的标准)。因此,虽然不同操作系统的IPC机制可能不尽相同,但其基本原理则并无大的不同。如果需要了解具体操作系统的IPC机制的实现。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。