我有一个代码可以提供一些数据,对文件执行一些操作等。 我使用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会更好吗?或者我想念对原子的用法的理解。
答案 0 :(得分:3)
std::atomic
没有为矢量重载。它也不是可复制的或可移动的(from here和here),因此一个人不能将它用作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)
在您的代码中,您使用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。