不要这样做。
创建一个每个方法都带有锁的线程安全集合类几乎是不可能的。
考虑您建议的 Concurrent 类的以下实例。
Concurrent<vector<int>> vi;
开发人员可能会这样做:
int result = 0;
if (vi.size() > 0)
{
result = vi.at(0);
}
另一个线程可能会在第一个线程调用之间进行此更改size()
and at(0)
.
vi.clear();
所以现在,同步操作顺序是:
vi.size() // returns 1
vi.clear() // sets the vector's size back to zero
vi.at(0) // throws exception since size is zero
因此,即使您有一个线程安全的向量类,两个竞争线程也可能导致在意外的地方抛出异常。
这只是最简单的例子。还有其他一些方式,多个线程尝试同时读/写/迭代可能会无意中破坏线程安全的保证。
您提到整个事情的动机是这种模式很麻烦:
vi_mutex.lock();
vi.push_back(1);
vi_mutex.unlock();
事实上,有一些帮助器类可以使这个更干净,即lock_guard,它将使用互斥锁来锁定其构造函数并在析构函数上解锁
{
lock_guard<mutex> lck(vi_mutex);
vi.push_back(1);
}
那么实践中的其他代码就变得线程安全了:
{
lock_guard<mutex> lck(vi_mutex);
result = 0;
if (vi.size() > 0)
{
result = vi.at(0);
}
}
Update:
我编写了一个示例程序,使用您的 Concurrent 类来演示导致问题的竞争条件。这是代码:
Concurrent<list<int>> g_list;
void thread1()
{
while (true)
{
if (g_list->size() > 0)
{
int value = g_list->front();
cout << value << endl;
}
}
}
void thread2()
{
int i = 0;
while (true)
{
if (i % 2)
{
g_list->push_back(i);
}
else
{
g_list->clear();
}
i++;
}
}
int main()
{
std::thread t1(thread1);
std::thread t2(thread2);
t1.join(); // run forever
return 0;
}
在非优化的构建中,上面的程序会在几秒钟内崩溃。 (零售有点困难,但错误仍然存在)。