在共享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';
}
我知道这不能证明什么,但它让我对这是安全的希望。如果没有标准的确认,我不确定我是否可以使用它。
答案 0 :(得分:2)
当另一个线程写入到列表时,读取操作都不是线程安全的(有关详细说明,请参阅here);所以:多次写入不是线程安全的:
从没有锁定的列表中读取时不会破坏列表,如果在另一个线程正在读取列表时修改了列表,则任一线程都可能会损坏(即崩溃或产生不正确的结果)。
你需要某种锁定。周期。
答案 1 :(得分:1)
在共享
@Html.Partial("Menu")
中,多个线程调用insert是安全的 同时,如果他们保证永远不会用相同的方式调用它 位置迭代器?
没有。它不安全......(无论如何)。
插入std::list
将需要访问std::list
和previous 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:
当表达式的评估写入内存位置时 另一个评估读取或修改相同的内存位置, 据说这些表达方式存在冲突。