【发布时间】:2014-01-07 04:11:02
【问题描述】:
我目前正在尝试解决使用 C++ STL 容器的线程安全问题。我最近尝试通过使用 std::mutex 作为成员变量来实现线程安全的 std::vector,然后才意识到虽然我可以通过锁定锁使成员函数成为线程安全的,但我无法使 lib 函数像 std::sort 线程安全,因为它们只获得 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 并不关心成员互斥锁。这是一项功能,而不是错误。
这让我的线程陷入困境,因为其他线程可以在某人做他的 lower_bound 事情时更改向量。
我能想到的唯一解决方法是:忘记包装器并为向量设置一个全局互斥锁。每当有人想对这个向量做任何事情时,他都需要那个锁。
这就是问题所在。使用此全局互斥体有哪些替代方法。 以及想到软件事务内存的地方。
那么现在:如何在 STL 容器上使用 STM? (和a),b),c)从上面)。
【问题讨论】:
-
std::vector已经是线程安全的。有关详细信息,请参阅stackoverflow.com/questions/9042571/…。 -
不......当我说“线程安全”时,这不是我的意思。我的意思是像真正的线程安全,而不是只读的废话。
标签: c++ multithreading c++11 transactional-memory