有谁知道用于 c++ 的快速而肮脏的线程安全向量类?我正在对一些代码进行多线程处理,我相信我遇到的问题与向量的使用方式有关。我计划重写代码,但在我疯狂地重做代码之前,我想用线程安全向量对其进行测试以确保。我还想如果有这样的东西,它会比编写我自己的版本容易得多。
6 回答
由于算法,这很困难。
假设您包装了 vector 以便它的所有成员函数都使用互斥锁序列化,就像 Java 同步方法一样。然后对该向量的并发调用std::remove
仍然不安全,因为他们依赖于查看向量并根据他们看到的内容进行更改。
因此,您的 LockingVector 需要专门化标准算法中的每个模板,以锁定整个事物。但随后其他算法std::remove_if
会在锁定下调用用户定义的代码。一旦有人开始创建对象向量,这些对象本身在内部对所有方法进行锁定,就在幕后默默地执行此操作是锁定反转的秘诀。
回答您的实际问题:对不起,不,我不知道。对于您需要的那种快速测试,我建议您从以下内容开始:
template <typename T>
class LockedVector {
private:
SomeKindOfLock lock;
std::vector<T> vec;
};
然后将其作为替换容器放入,并开始实现成员函数(以及成员 typedef 和运算符),直到它编译为止。您会很快注意到,如果您的任何代码在向量上使用迭代器,而这种方式根本无法从内到外实现线程安全,并且如果需要,您可以在这些情况下临时更改调用代码以锁定向量通过公共方法。
您可以查看TBB(如concurrent_vector)。我从来没有使用过它,老实说,我发现将范围保护对象放在访问周围更容易(特别是如果向量被正确封装)。
我认为您会发现继续使用 std::vector 会容易得多,但使用某种互斥锁或其他操作系统同步对象来保护并发访问。如果您使用互斥锁,您肯定也想使用 RAII。
正如 Scott Meyers 在有效的 STL 书中解释的那样,通过线程安全容器,您可以期望:
- 多次读取是安全的
- 对不同容器的多次写入是安全的。
就这样。您不能指望许多其他事情,例如对同一个容器的多次写入是线程安全的。如果这就是你想要的,那么你可以看看STLPort。如果不是,那么我看到的唯一选择是将向量包含在一个同步对向量的访问的类中。
我忘记了谁讨论过这个,但是制作线程安全容器的一种策略如下:
- 所有类的公共方法都必须锁定向量,并且如果它们成功(并且它们可能不会成功!),则必须返回一个布尔值。因此,不要使用
f = myvec[i]
,而是if (myvec.tryGet(i, &f)) {...}
相应地使用和实现该类。 - 不提供计数方法。用户必须使用迭代器来遍历向量。
注意:小心迭代。您必须很聪明地使用边界检查迭代器维护一个永不收缩的向量,否则您的代码可能会出现缓冲区溢出类型错误。
提供“线程安全”向量的蹩脚且简单的方法是仅采用标准向量并将该向量锁定在每个方法上。但是如果你这样做,你仍然可能会得到损坏的代码(例如,从 0 迭代到 vec.count 的循环在迭代时可能会改变计数)。
提供“线程安全”容器的第二种方法是创建不可变容器(每个方法都返回一个新容器。这肯定由 Eric Lippert 讨论过。它是 C#,但大多数情况下很容易转换为 C++ 代码。你仍然需要使用时锁定容器,但是当迭代器中断时,所有涉及缓冲区溢出的可怕问题都会消失。实现一个不可变容器对于那些有函数式编程经验的人来说可能是相对的第二本能。
如果您还没有,请考虑使用tbb库中的 concurrent_vector。C++ STL 向量不是线程安全的,因此如果您计划从多个线程修改向量资源,我发现最简单的解决方案是使用 concurrent_vector。