由于同一进程中的线程共享相同的地址空间,因此我们可以通过直接内存访问和互斥锁在这些线程之间传递数据。在这个背景下,我有以下问题:
- 全局变量和互斥锁是否足以实现线程间通信?
- 如果问题1的答案是否定的,那么在什么情况下我们应该选择其他的IPC(进程间通信)而不是直接内存访问呢?或者说,在什么情况下使用其他IPC方法比使用全局变量和互斥锁更合适?
谢谢。
更新
感谢 @ssyam 指出关于“全局变量”的错误陈述。
我选择添加一个额外的部分来除了纠正原始段落之外,因为已经有很多评论针对该段落。
由于同一进程中的线程共享相同的地址空间,因此我们可以通过直接内存访问和互斥锁在这些线程之间传递数据。在这个背景下,我有以下问题:
谢谢。
更新
感谢 @ssyam 指出关于“全局变量”的错误陈述。
我选择添加一个额外的部分来除了纠正原始段落之外,因为已经有很多评论针对该段落。
struct Thread
{
Thread() : m_thread(&Thread::run, this) {}
void run()
{
// access the current object's member variables, eg:
do_something_with(m_myvar);
}
Object m_myvar;
std::thread m_thread;
};
condition_variable
(作为唤醒触发器)几乎总是正确的方法。我自己几乎总是使用线程安全的消息队列(即std::queue + mutex + condition_variable
)在线程之间进行通信(生产者/消费者模式),这是一种既隔离线程又允许它们相互通信的非常有效的方式。
在(1)上,我同意Dietmar Kühl的看法,认为条件变量是该最小集合的一部分。
在(2)上,我倾向于选择IPC,只要我能够承受开销的小成本(主要是系统调用和一些数据复制),因为它们带来了便捷和灵活性。 管道、消息队列、域套接字等都具有原子性和同步功能,并且根据情况提供阻塞、非阻塞或定时读/写。 而且,您可以将它们全部放入select
语句中,而无需进行任何特殊操作。 这是小成本获得的大量功率,并且不需要重新发明轮子。
这里有一个想法:
尽管两个线程正在访问同一块内存,但并不一定意味着它们看到的是相同的值。如果一个线程正在更新值,则另一个线程可能会看到陈旧的值 - 更新之前从处理器本地缓存中取出的值。为了防止这种情况发生,您需要使用互斥锁或其他技术来同步线程。
无论使用哪种技术,都必须使用所谓的“内存屏障”来刷新本地缓存,这是一项非常昂贵的操作,因为它将要求所有处理器停止它们正在进行的任何操作并等待操作完成。
另一方面,IPC调用并不一定需要这样做。
全局变量在单线程代码中很糟糕,在多线程代码中通常会成为一个主要问题。即使使用互斥锁进行同步,它们也往往会成为瓶颈。此外,互斥锁通常不足以实现线程间通信。通常,您还需要条件变量。
话虽如此,在多线程应用程序中,在内存中传输数据是合理的。但是,我发现处理显式锁定通常不可行。当将数据传输类似于消息传递系统时,代码往往较少复杂且更有效率,即使消息是内存中的数据结构。从这个意义上说,一条消息在任何时候只被一个线程使用,并且唯一发生的锁定是隐含在消息传递设施中的。