std :: forward_list :: insert_after线程安全

时间:2017-02-17 13:23:58

标签: c++ multithreading thread-safety c++14 forward-list

在共享std::forward_list中,如果多个线程保证永远不会使用相同的位置迭代器调用它,那么它是否可以同时调用insert_after?看起来这可能是安全的,因为插入保证不会使其他迭代器无效,并且容器没有size()方法,但也许我错过了什么?

编辑:

我写了一个小酷刑测试程序,似乎在没有任何锁定的情况下在Clang中正常运行:

#include <forward_list>
#include <iostream>
#include <thread>
#include <vector>

using List = std::forward_list< int >;
using It = List::const_iterator;

void insertAndBranch (List& list, It it, int depth)
{
    if (depth-- > 0) {
        It newIt = list.insert_after (it, depth);
        std::thread thread0 ([&]{ insertAndBranch (list, it, depth); });
        std::thread thread1 ([&]{ insertAndBranch (list, newIt, depth); });
        thread0.join();
        thread1.join();
    }
}

int main()
{
    List list;
    insertAndBranch (list, list.before_begin(), 8);

    std::vector< It > its;
    for (It it = list.begin(); it != list.end(); ++it) {
        its.push_back (it);
    }
    std::vector< std::thread > threads;
    for (It it : its) {
        threads.emplace_back ([&]{ list.insert_after (it, -1); });
    }
    for (std::thread& thread : threads) {
        thread.join();
    }

    for (int i : list) {
        std::cout << i << ' ';
    }
    std::cout << '\n';
}

我知道这不能证明什么,但它让我对这是安全的希望。如果没有标准的确认,我不确定我是否可以使用它。

2 个答案:

答案 0 :(得分:2)

当另一个线程写入到列表时,读取操作都不是线程安全的(有关详细说明,请参阅here);所以:多次写入不是线程安全的:

从没有锁定的列表中读取时不会破坏列表,如果在另一个线程正在读取列表时修改了列表,则任一线程都可能会损坏(即崩溃或产生不正确的结果)。

你需要某种锁定。周期。

答案 1 :(得分:1)

  

在共享@Html.Partial("Menu")中,多个线程调用insert是安全的   同时,如果他们保证永远不会用相同的方式调用它   位置迭代器?

没有。它不安全......(无论如何)。

插入std::list将需要访问std::listprevious node到迭代器位置以及列表的next node

即使这些位置相互远离,std::list::size()需要恒定时间(C ++ 11)这一简单事实。这意味着每次插入都会更新size

编辑:

  

在共享std::list::size()中,多线程是安全的   如果保证永远不会调用它,则同时调用insert_after   具有相同位置的迭代器?

不安全。而不是推荐。没有STL容器设计有线程安全性。

无论如何,让我们假设一些基本的保证,让我们假设一个非常简单的std::forward_list版本:std::forward_list修改迭代器指向的节点,以便节点现在指向新插入的节点,新插入的节点指向下一个节点。所以它只会是'#34;安全&#34;如果迭代器至少有两个节点彼此远离,并且你的分配器是线程安全的。

插图:

insert_after

如您所见,Initial Forward_list A -> B -> C -> D -> E Insert K after C A -> B -> C x D -> E \ / K 已被阅读和修改。可以读取C,同时插入D

至于为什么我说&#34;至少有两个节点&#34;,让我们假设一个节点离开:假设两个线程想要在K之后插入K,并且{分别在C之后{1}}:

M

来自cppreference

  

当表达式的评估写入内存位置时   另一个评估读取或修改相同的内存位置,   据说这些表达方式存在冲突。