4

由于同一进程中的线程共享相同的地址空间,我们可以通过直接内存访问和互斥锁在这些线程之间传输数据,那么在这种情况下,我有以下问题:

  1. 全局变量和互斥体是否足以进行线程间通信?
  2. 如果问题 1 为假,在什么情况下我们应该选择其他 IPC 而不是直接内存访问?或者说,在什么情况下其他 IPC 手段比使用全局变量和互斥体更合适?

谢谢。

更新
感谢@ssyam 指出关于“全局变量”的错误陈述。
除了更正原始段落之外,我选择再添加一个部分,因为已经对该段落进行了许多评论。

4

4 回答 4

1

不需要全局变量。请记住,线程例程可以接受参数,因此它可以是任何类型的变量,包括动态分配的变量。

通常你会想要将你的线程“包装”在一个类中,类似于:

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但是,如果我们把全局变量的这个小细节放在一边,你的 #1 是对的......通过受互斥锁和可选的(作为唤醒触发器)保护的变量(无论是消息队列、布尔值等)进行通信几乎总是要走的路。

我几乎总是最终使用线程安全的消息队列(即。std::queue + mutex + condition_variable)在线程之间进行通信(生产者/消费者模式),这是隔离线程并允许它们通信的一种非常有效的方式。


事实上,在单个进程中,除了直接内存访问之外,很少有其他的东西有意义。

我现在能想到的是,如果你已经有一些工作的进程间代码(例如套接字或共享内存),你可以重用这些代码以允许一个统一的接口,无论是进程内还是进程间通讯。但是不要自欺欺人,直接内存访问效率会降低。然而,统一界面的好处可以轻松克服效率损失。恕我直言,您确实需要根据具体情况处理此类事情。

于 2013-08-25T01:33:20.550 回答
1

关于 (1) 我同意 Dietmar Kühl 的观点,即条件变量是该最小集的一部分。

关于(2)我倾向于选择 IPC,只要我能负担得起开销的小成本(主要是系统调用和一些数据复制),因为它们带来的易用性和灵活性。管道、消息队列、域套接字等都具有内置的原子性和同步性,它们根据情况需要提供阻塞、非阻塞或定时读/写。你可以把它们塞进一个select语句而不做任何特别的事情。它以很小的成本提供了很多功能,并且不涉及重新发明轮子。

于 2013-08-25T02:39:08.467 回答
0

这是一个想法:

即使两个线程访问相同的内存并不一定意味着它们看到相同的值。如果一个线程正在更新值,那么另一个线程可以看到陈旧的值——更新之前从处理器本地缓存中获取的值。为了防止这种情况发生,您需要使用互斥锁或其他一些技术来同步线程。

无论采用何种技术,都必须使用所谓的“内存屏障”来刷新本地缓存,这是一项非常昂贵的操作,因为它需要所有处理器停止它们正在做的任何事情并等待操作完成。

另一方面,IPC 调用不一定要求这样做。

于 2013-08-25T01:31:12.050 回答
0

好吧,全局变量在单线程代码中很糟糕,在多线程代码中它们通常会成为一个主要问题。即使使用互斥锁正确完成同步,它们也往往会成为瓶颈。此外,互斥锁通常不足以进行线程间通信。您通常至少还需要条件变量。

也就是说,在多线程应用程序中,在内存中的线程之间传输数据是合理的。但是,一般来说,我发现处理显式锁定是不可行的。在传输类似于消息传递系统的数据时,即使消息是内存中的数据结构,代码也会变得不那么复杂,效率更高。从这个意义上说,一条消息在任何时候都只能由一个线程使用,唯一的锁定发生在消息传递设施中。

于 2013-08-25T01:32:58.610 回答