使用atomic而不是CriticalSection?

时间:2017-10-27 12:04:03

标签: c++ multithreading winapi

我有一个代码可以提供一些数据,对文件执行一些操作等。 我使用1个线程来提供数据,使用4个线程来读取和搜索数据。

4个线程使用文件向量进行搜索(使用相同的功能)。为了避免同步问题(所有线程同时读取同一个文件)我使用CriticalSection() WinAPI:

void ReadData(char* fileName)
{
   EnterCriticalSection(&CriticalSection);

   // Open file

   // Read file data
   std::vector<std::string> data;

   ... Find data inside file

   // Close file

   LeaveCriticalSection(&CriticalSection);
}

但是我看到了Post 这个:

  

原子类型的对象是唯一没有数据竞争的C ++对象;也就是说,如果一个线程写入原子对象而另一个线程从中读取,则行为是明确定义的。

我的问题是:使用std::atomic代替CriticalSection会更好吗?或者我想念对原子的用法的理解。

2 个答案:

答案 0 :(得分:3)

std::atomic没有为矢量重载。它也不是可复制的或可移动的(from herehere),因此一个人不能将它用作std :: vector value_type(或者可能需要提供你自己的专门化,我可以没有(也不会)尝试/编辑。

但是,如果您希望代码与平台无关,std::mutex可以完美地完成,例如:

std::mutex myMutex_; //Typically a member or static or in unnamed namespace

void ReadData(char* fileName)
{
   std::lock_guard<std::mutex> guard(myMutex_);
   // Open file

   // Read file data
   std::vector<std::string> data;

   ... Find data inside file

   // Close file

   //... Releases when scoped left...
}

从某种意义上说,我的回答是回声this previous post

答案 1 :(得分:3)

使用标准C ++

在您的代码中,您使用Microsoft的WinAPI的EnterCriticalSection()LeaveCriticalSection()功能。

这些有很多不便之处:首先它们不可移植,其次它们不安全:如果异常导致线程以意外的方式离开ReadData()会发生什么?你可能最终得到一个关键部分,看起来不会留下窗户,使所有其他线程都挨饿!

lock_guard上使用mutex的标准C ++替代方案,如Werner所示,更加安全:首先它可跨平台移植,但此外,它实现了{ {3}} idiom,确保如果出现意外异常,则在保留函数时会销毁lock_guard,从而导致mutex被释放。

使用原子可能还不够

很多时候,人们很想使用原子,因为它们可以避免数据竞争,并给人一种印象,即它将解决所有线程同步问题。

不幸的是,事实并非如此。一旦你使用了几个原子,你就可以对整体一致性做出假设,而实际上事情可能会发生不同并导致非常讨厌的错误。创建无锁算法和数据结构极具挑战性和困难。因此,我强烈建议阅读Anthony William的优秀书籍&#34; C ++并发行动&#34; :他全面深入探讨了所有相关方面。

其他替代方案

在你的问题中,你指的是一个向量。维护并发线程安全向量非常困难,因为基本上,每当向量的容量必须被扩展时,可能会发生重新分配,使所有迭代器和指向该向量的指针无效,无论它们在何处使用。幸运的是,有一些线程安全的实现,如Microsoft&#39; RAII

另一方面,如果您仅使用向量来存储文件的行并按顺序处理它们,那么您也可以考虑使用队列,其优点是能够使用众多线程安全实现之一可用,例如Parallel Pattern Library