列表和多线程环境

Lists and multithreaded environments

本文关键字:环境 多线程 列表      更新时间:2023-10-16

我对标准库相当陌生C++并且一直在使用标准库列表进行特定的多线程实现。我注意到使用我在任何教程/博客/论坛帖子中都没有看到的列表可能会有一个技巧,虽然对我来说似乎很明显,但似乎没有人考虑过。所以也许我太新了,可能会错过一些东西,所以希望比我聪明的人可以验证我想要实现的目标或向我解释我做错了什么。

所以我们知道,在一般情况下,标准库容器不是线程安全的 - 但这似乎是一个指导性声明,而不是一个规则。对于列表,似乎对线程安全有一定程度的容忍度。让我解释一下,我们知道如果我们从列表中添加/删除列表,列表不会失效。唯一失效的迭代器是已删除的项 - 您可以使用以下代码行修复它:

it = myList.erase(it)

所以现在假设我们有两个线程,并将它们称为线程 1 和线程 2。

线程 1 的职责是添加到列表中。它将其视为队列,因此它使用 std::list::p ush_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 知道当前最后一个元素的范围是灵活的,则它也可以避免锁定。

有人认为我的建议有什么问题吗?

谢谢!

你的程序很活泼。 举一个明显的数据竞赛的例子:std::list不仅仅是双链接节点的集合。 例如,它还具有一个存储列表中节点数的数据成员(它不需要是单个数据成员,但它必须将计数存储在某个位置)。

两个线程将同时修改此数据成员。 由于这些修改没有同步,因此您的程序是不可靠的。

标准库容器的实例不能在没有外部同步的情况下同时从多个线程进行突变。