我是C ++标准库的新手,并且一直在使用标准库列表来实现特定的多线程实现。我注意到使用我在任何教程/博客/论坛帖子上都没有看过的列表可能会有一些技巧,虽然对我来说似乎显而易见,似乎没有人考虑过。所以也许我太新了,可能会遗漏一些东西,所以希望比我聪明的人可以验证我想要实现的目标,或者向我解释我做错了什么。
所以我们知道,通常标准库容器不是线程安全的 - 但这似乎不仅仅是一个规则的指导性声明。使用列表似乎对线程安全有一定程度的容忍度。让我解释一下,我们知道如果我们从列表中添加/删除列表不会失效。失效的唯一迭代器是已删除的项目 - 您可以使用以下代码行修复该项目:
it = myList.erase(it)
现在假设我们有两个线程并将它们称为线程1和线程2.
线程1的职责是添加到列表中。它将其视为队列,因此它使用std :: list :: push_back()函数调用。
线程2的职责是将存储在列表中的数据作为队列进行处理,然后在处理后将从列表中删除元素。
它保证线程2不会删除列表中刚刚在处理过程中添加的元素,并且线程1保证它将在线程2处理之前提前排队必要的数据。但是,请记住,在线程2的处理过程中可以添加元素。
因此,似乎在这个多线程环境中合理使用列表而不使用锁来进行数据保护。我说它合理的原因是因为,从本质上讲,线程2将只处理数据,以便它可以检索由以下伪代码显示的当前结束迭代器:
Thread 2 {
iter = myList.begin();
lock();
iterEnd = myList.end(); // lock data temporarily in order to get the current
// last element in the list
unlock();
// perform necessary processing
while (iter != iterEnd) {
// process data
// ...
// remove element
iter = myList.erase(iter);
}
}
线程2使用锁定的时间非常短,只知道停止处理的位置,但大多数情况下,线程1和线程2不需要任何其他锁定。此外,如果线程2知道当前最后一个元素的范围是灵活的,那么线程2也可以避免锁定。
有人看到我的建议有什么问题吗?
谢谢!
答案 0 :(得分:5)
你的节目很活泼。作为一个明显的数据竞争的例子:std::list
不仅仅是一个双重链接节点的集合。例如,它还有一个数据成员,用于存储列表中的节点数(它不需要是单个数据成员,但必须在某处存储计数)。
您的两个线程将同时修改此数据成员。因为这些修改没有同步,所以你的程序很活泼。
如果没有外部同步,标准库容器的实例不能同时从多个线程变异。