我目前正在尝试使用C ++ STL容器解决线程安全问题。我最近试图通过使用std :: mutex作为成员变量来实现线程安全的std :: vector,然后才意识到虽然我可以通过锁定锁来使成员函数成为线程安全的,但我无法创建lib函数像std :: sort thread-safe一样,因为它们只获取begin()/ end()迭代器,这是STL中容器和算法之间基本分割的结果。
那么我想,如果我不能使用锁,软件事务内存(STM)怎么样?
所以现在我坚持这个:
#include <atomic>
#include <cstdlib>
#include <iostream>
#include <thread>
#include <vector>
#define LIMIT 10
std::atomic<bool> start{false};
std::vector<char> vec;
void thread(char c)
{
while (!start)
std::this_thread::yield();
for (int i = 0; i < LIMIT; ++i) {
__transaction_atomic {
vec.push_back(c);
}
}
}
int main()
{
std::thread t1(thread, '*');
std::thread t2(thread, '@');
start.store(true);
t1.join();
t2.join();
for (auto i = vec.begin(); i != vec.end(); ++i)
std::cout << *i;
std::cout << std::endl;
return EXIT_SUCCESS;
}
我编译的是:
g++ -std=c++11 -fgnu-tm -Wall
使用g ++ 4.8.2,它给出了以下错误:
error: unsafe function call to push_back within atomic transaction
我有点得到,因为push_back或sort或其他任何未声明的transaction_safe,但它留下了以下问题:
a)如何解决该错误?
b)如果我无法解决该错误,那么这些事务块通常用于什么?
c)如何实现无锁线程安全向量?!
提前致谢!
修改 感谢目前为止的答案,但他们并没有真正划伤我的痒。让我给你举个例子: 想象一下,我有一个全局向量,并且应该在多个线程之间共享对该向量的访问。所有线程都尝试进行排序插入,因此它们生成一个随机数并尝试以排序方式将此数字插入向量中,因此向量始终保持排序(包括重复的c)。要执行已排序的插入,他们使用std :: lower_bound来查找要插入的“索引”,然后使用vector.insert()进行插入。
如果我为包含std :: mutex作为成员的std :: vector编写一个包装器,那么我可以编写包装函数,例如insert使用std :: lock_guard锁定互斥锁,然后执行实际的std :: vector.insert()调用。但是std :: lower_bound并没有对成员互斥量有所了解。这是一个功能,而不是一个bug。
这使得我的线程非常糟糕,因为其他线程可以在某人正在执行他的lower_bound事情时更改向量。
我能想到的唯一解决办法:忘记包装器并为矢量设置一个全局互斥锁。每当有人想要对这个向量进行任何操作时,他都需要锁定。
这就是问题所在。使用这种全局互斥锁有哪些替代方案。 这就是软件事务记忆的想法。
现在:如何在STL容器上使用STM? (和a),b),c)来自上面)。
答案 0 :(得分:2)
我相信你可以使STL容器100%线程安全的唯一方法是将它包装在你自己的对象中(保持实际的容器私有)并在你的对象中使用适当的锁定(互斥,等等)以防止多线程访问STL容器。
这就是在每个容器操作周围锁定调用程序中的互斥锁的道德等同物。
为了使容器真正具有线程安全性,您必须使用容器代码,而这些代码没有任何规定。
编辑:还有一点需要注意 - 请注意您为包装器对象提供的界面。你不能很好地分发对存储对象的引用,因为这将允许调用者绕过包装器的锁定。因此,您不能仅仅使用互斥锁复制vector的接口,并希望工作正常。
答案 1 :(得分:0)
我不确定我理解为什么你不能使用互斥锁。如果每次访问向量时都锁定互斥锁,那么无论您正在执行什么操作,您都确定一次只有一个线程正在使用它。根据您对安全载体的需求,肯定有改进的空间,但是互斥体应该是完全可行的。
锁定互斥锁 - &gt;调用std :: sort或者你需要的任何东西 - &gt;解锁互斥锁
如果在另一方面你想要的是在你的类上使用std :: sort,那么再次通过容器的迭代器提供线程安全的访问和读取方法,因为那些是那些std :: sort无论如何都需要使用才能对向量进行排序,因为它不是容器的朋友或任何类型的东西。
答案 2 :(得分:0)
您可以使用简单的互斥锁使您的类线程安全。如另一个答案所述,您需要使用互斥锁在使用前锁定矢量,然后在使用后解锁。
小心!所有STL函数都可以抛出异常。如果你使用简单的互斥锁,如果任何函数抛出你将会遇到问题,因为互斥锁不会被释放。要避免此问题,请将互斥锁包装在一个在析构函数中释放它的类中。这是一个很好的编程实践,可以了解:http://c2.com/cgi/wiki?ResourceAcquisitionIsInitialization