我怀疑,除了堆栈之外,线程共享进程的所有部分。所以,要在线程之间进行通信,假设我想传递一个词" Hello"从一个线程到另一个线程,IPC机制的需求是什么(例如消息队列)。
答案 0 :(得分:9)
在进程中的线程之间以及其他进程中的线程之间存在各种方式来传递数据。您只需选择一个适合您需求的产品。
共享内存
单个进程中的线程可以访问进程中的所有内存,但正如您所说它们有自己的堆栈。这意味着一个线程与另一个线程共享一块内存是相当直接的。通常你会使用某种形式的信号量来确保它们不会同时尝试访问内存(这通常会导致非常混乱的程序行为......)。
代码快速高效,因为数据永远不会被复制,但是很难正确地获得复杂的程序。
消息队列,管道等
但是,像消息队列这样的东西是一种从一个线程使用的内存向另一个线程使用的内存发送数据的方法。
有许多优点,因为使用内存队列而不是共享内存来编写复杂程序通常要容易得多。但是,内存队列固有的效率较低。这是因为操作系统中的消息队列实现必须复制数据(通常是两次)。
主题与流程
使用共享内存方法,将所有线程放在单个进程中是很自然的,而对于消息队列,将线程放在单独的进程中会更自然。但这并不重要。大多数操作系统允许进程将其部分内存暴露给其他进程(Linux上的/ dev / shm),并且任何进程都可以获取命名信号量。
从架构上讲,“一个进程中的所有线程”与“自己进程中的所有线程”的问题并不重要。您可能需要记住操作系统的线程与进程上下文切换时间,因为这也会影响效率。
的可扩展性
如果您选择像管道这样的IPC机制,那么就有可能实现可扩展性。特别是管道的行为与套接字非常相似。因此,如果您的程序在您的计算机上运行速度太慢,那么将管道转换为套接字并将线程放在分布在两台或多台计算机上的进程中并不是那么多工作。因此,使用管道意味着 难以将您的应用程序转变为多计算机分布式应用程序。
当然,套接字要慢得多,所以你必须考虑在确定之前每秒需要从一个线程发送到另一个线程的每秒数据。
CPU内存架构
还记得我说内存队列的效率低于共享内存吗?好吧,这些日子并没有那么明确。英特尔目前的架构,特别是AMD,意味着复制数据与阅读数据并没有太大差别。
看看英特尔芯片之间的QPI链接,并考虑电子产品层面的实际情况。假设您的计算机中有两个已填充的CPU插槽,并且您的应用程序有两个共享内存缓冲区的线程。
对于一个芯片上的线程来访问驻留在另一个芯片的内存中的数据,它必须通过QPI链接读取该数据。
现在,假设这些线程使用了消息队列。操作系统必须将数据从一个线程复制到另一个线程。这意味着通过QPI链接读取数据。它在QPI链接上的活动大致相似。
AMD的架构让它变得更加明显;他们的等效(和优秀的......)QPI被称为Hypertransport,它在AMD的芯片内运行。这不是那么直截了当。缓存使事情变得复杂得多。但是对于一些程序来说,有很多线程分布在两个或多个共享大量数据的芯片上,QPI有可能减慢一切。英特尔当然知道并设计它,以便通常QPI不是瓶颈。但是,如果您的程序不是英特尔在选择QPI参数时所考虑的,那么性能可能会低于预期。
有效地,在某些情况下并且可以非常谨慎地使用管道,消息队列等,并且最终比使用共享内存的程序具有更好的性能。
但是,出于简单性和可靠性的原因,我仍然会选择管道和消息队列。
答案 1 :(得分:8)
通过阅读好Pthreads Tutorial,你真的应该花几个小时来学习更多知识。您可能应该了解更多基本的IPC(Inter-Process Communication)机器,例如阅读Advanced Linux Programming
使用支持最新C++11标准的C ++编译器,您可以使用其C++ thread库(通常在pthreads之上构建)。您需要GCC 4.8或更高版本或Clang 3.3或更高版本(使用-std=c++11
标记到g++
或clang++
编译器。
某些threads的所有process共享address space {并且经常使用硬件辅助cache coherence)通过某些memory model。
我建议您详细了解proc(5)。命令cat /proc/1234/maps
显示pid 1234进程的地址空间。
特别是,堆栈实际上并不是每个线程的隔离段:换句话说,线程可以访问某个其他线程堆栈上的某些数据。例如,您可以在其堆栈上创建一个带有本地 int x;
变量的线程,在某处写入地址&x;
(例如,在g
的全局变量中g = &x;
1}} C语句)并有另一个线程解引用指针通过*g
(但这是一个糟糕的编程风格)。给定进程的所有线程堆栈仍在同一地址空间中。
但是,你真正需要的是synchronize个线程。 pthreads的一个主要目的是为您提供多种同步方式(mutexes和condition variables,barriers,semaphores等等。最近的语言标准也有atomicity构造,如C ++ 11的std::atomic或C11的stdatomic.h。小心race conditions和deadlocks!由于可能heisenbugs,调试多线程(或其他并行)程序很痛苦。你可能应该赞成functional programming风格。
多线程编程很难,因为parallel programming很难。
您可以在线程之间使用IPC机制,例如,您可以拥有pipe(2)并且有一个线程写入它而另一个线程读取它(可能与poll(2)多路复用...)。但是人们在编码线程时通常更喜欢使用pthread机制(例如,有一个全局数据链接一些链表并通过locking互斥锁连续访问它)。然后,您需要使用条件变量发出从空变换到非空的信号,并在从(空)链接列表中获取元素时等待该条件。这是producer-consumer情况。您可以拥有一个共享队列或列表等...用于像"Hello World"
这样的字符串,但您仍然需要同步线程。