【问题标题】:One thread writes to a variable the other thread reads the variable how do I (pre-C++11) protect that variable?一个线程写入一个变量,另一个线程读取该变量我如何(C++ 11 之前)保护该变量?
【发布时间】:2016-10-19 14:07:01
【问题描述】:

我在 C++11 之前工作,否则我只会使用包含线程和原子变量来满足我的需求,但是,不能这样做。有一个类,当实例化时会启动多个线程。在一个线程启动的函数中,我有类似的东西:

void ThisClass::ThisThread()
{
    while (runThisThread)
    {
        // doing stuff
    }
}

另外一个函数是:

void ThisClass::StopThisThread()
{
    runThisThread = false; // 'runThisThread' variable is 'volatile bool'
}

一个线程将根据从另一个线程分配的索引来咀嚼缓冲区。因此,一个线程会分配一个值,而另一个线程除了读取该值外永远不会做任何事情。我的计划是使用更易失的内存来分配这些索引值。但是,这个问题表明我错误地使用了易失性内存When to use volatile with multi threading?。在 C++11 之前处理这样的多线程类中的内存的正确方法是什么?请记住,我不允许多个线程分配单个变量,而每个线程都可以读取该变量。

编辑:忘了包括这是一个不需要跨平台的 Windows 程序。我正在使用 afxwin.h AfxBeginThread() 进行线程处理。

【问题讨论】:

  • 您需要原子变量或互斥体。我把它作为练习留给 OP 来研究它们。
  • 使用您使用的线程 API 中的互斥锁。
  • 你使用的 API 应该有一些教程。应该在某处提到互斥锁或同步机制
  • 由于线程不是 C++ 98 的一部分,因此您必须为此使用库,例如​​ pthreads。该库还将提供用于同步和内存屏障的设施。使用这些。
  • Microsoft 扩展 volatile 以适应线程间类似原子的值共享,因此声明 runThisThread volatile 应该做你想做的事,只要 bool 值可以“复制”在一条硬件指令中”,并且您正在使用 /volatile:ms 选项(非 ARM 架构的默认值)进行编译。见:msdn.microsoft.com/en-us/library/12a04hfd.aspx

标签: c++ windows multithreading mfc c++03


【解决方案1】:

这种情况最好使用手动重置event object(或相应的CEvent MFC 包装器)来解决。当您想终止线程时,您只需发出事件信号。线程循环应该评估事件状态:

while( ::WaitForSingleObject( hEvent, 0 ) == WAIT_TIMEOUT ) {
    // doing stuff
}

或作为 MFC 版本:

while( !myEvent.Lock( 0 ) ) {
    // doing stuff
}

【讨论】:

  • 这适用于停止线程。然而,它并没有解决问题的第二部分。 “一个线程将根据从另一个线程分配的索引来咀嚼缓冲区。因此,一个线程将分配一个值,另一个线程除了读取该值外永远不会做任何事情。我的计划是使用更多易失性内存来分配这些索引值。但是,这个问题表明我错误地使用了易失性内存何时将易失性与多线程一起使用?”仍然需要一个 int 变量,一个线程分配,另一个线程读取。
  • @BradB.:当我读到这个问题时,我并不清楚。对于非便携式解决方案,您可以使用 volatile int 和编译器开关 /volatile:ms(非 ARM 架构的默认设置)。如果您想要跨编译器可移植的东西,您可以选择任何synchronization 工具(如 SRWL 或 InterlockedXyz API)。
  • 抱歉耽搁了。我已选择您的答案作为正确答案。我想为将来看到这个问题的任何人补充。任何 Windows 7 或更早版本都是非 ARM 架构。 Microsoft 在 Windows 7 之前不支持它,所以如果你在其中一台机器上,你可以安全地假设 /volatile:ms 是选定的选项。
【解决方案2】:

在保持代码不变的情况下处理此问题的最有效方法是使用互锁函数:

volatile DWORD runThisThread;

void ThisClass::ThisThread()
{
    while (InterlockedCompareExchange(&runThisThread, 0, 0))
    {
        // doing stuff
    }
}

void ThisClass::StopThisThread()
{
    InterlockedExchange(&runThisThread, false);
}

这比使用临界区保护变量或用事件对象替换变量要快得多。

(但是,如果您的循环需要空闲,例如在等待更多工作时,那么您应该使用事件对象来避免忙等待。)

【讨论】: