C ++线程安全 - 在worker和controller之间交换数据

时间:2010-03-17 17:34:49

标签: c++ visual-c++ thread-safety

我仍然觉得这个话题有点不安全,希望大家能帮助我 -

为了在工作线程轮询某事物和对最新数据感兴趣的控制线程之间传递数据(配置或结果),我最终重复使用了或多或少的以下模式:

Mutex m;
tData * stage;       // temporary, accessed concurrently

// send data, gives up ownership, receives old stage if any
tData * Send(tData * newData)
{
   ScopedLock lock(m);
   swap(newData, stage);              
   return newData;
}

// receiving thread fetches latest data here
tData * Fetch(tData * prev)
{
  ScopedLock lock(m);
  if (stage != 0)
  {
     // ... release prev
     prev = stage;
     stage = 0;
  }
  return prev;  // now current
}

注意:这不应该是完整的生产者 - 消费者队列,只有msot最新数据是相关的。另外,我在这里略微浏览了资源管理。

必要时我会使用两个这样的阶段:一个用于向工作人员发送配置更改,以及用于发回结果。

现在,我的问题

假设ScopedLock实现了完整的内存屏障:

  • 做舞台和/或workerData需要不稳定吗?
  • 是tData成员必需的?
  • 我可以使用智能指针而不是原始指针 - 比如boost::shared_ptr吗?
  • 还有其他可能出错的地方吗?
  • 我基本上试图避免“挥发性感染”传播到tData,并最小化锁争用(似乎也可以实现无锁实现)。但是,我不确定这是否是最简单的解决方案。

ScopedLock充当完整的内存屏障。由于所有这些都或多或少取决于平台,所以我们可以说Visual C ++ x86或x64,但其他平台的差异/注释也是受欢迎的。


(对于推荐像英特尔TBB这样的库来说,这是一个前期“谢谢” - 我试图了解这里的平台问题)

2 个答案:

答案 0 :(得分:2)

此处您不需要volatile。仅当值可能因程序之外的内容而发生更改时才使用volatile,例如变量表示内存映射的硬件寄存器。这里的值只在程序中修改,因此您可以信任编译器知道何时可以缓存值。

如果您需要确保工作人员和控制人员不同时访问共享数据,我建议您改为使用mutex。在SendFetch函数中,只需锁定互斥锁,操纵stage,然后释放互斥锁即可。我不知道你有哪些系统库,但有一个很好的描述POSIX互斥(来自pthreads)here。 Win32版本(尽管解释较少)可用here。其他库将使用不同的名称,但概念是相同的。

答案 1 :(得分:0)

这里有一个问题:

您的send函数需要通过引用(或指针指针)传入newData。否则,交换的结果永远不会使其返回给调用者。

您不需要volatile只是意味着每次访问数据时都会从内存中读取数据。由于你的程序总是在改变stage的值,编译器会知道发生了什么,一切都会好的。如果您的程序之外的某些内容更改了值,则只使用volatile。例如,您有一个串行端口,它将数据发送到内存中的某个位置,并且您的程序会轮询该内存以​​进行更新。每次轮询该内存时,都必须检查内存,而不是缓存,这就是你使用volatile的地方。