Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/149.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ std::forward_list::在线程安全后插入__C++_Multithreading_Thread Safety_C++14_Forward List - Fatal编程技术网

C++ std::forward_list::在线程安全后插入_

C++ std::forward_list::在线程安全后插入_,c++,multithreading,thread-safety,c++14,forward-list,C++,Multithreading,Thread Safety,C++14,Forward List,在共享的std::forward_列表中,如果多个线程保证从不使用相同的位置迭代器调用insert_,那么并发调用after是否安全?考虑到insert保证不会使其他迭代器失效,并且容器没有size()方法,这似乎是安全的,但可能我遗漏了什么 编辑: 我编写了一个小的酷刑测试程序,在没有任何锁定的情况下运行良好: #include <forward_list> #include <iostream> #include <thread> #include <

在共享的
std::forward_列表
中,如果多个线程保证从不使用相同的位置迭代器调用
insert_,那么并发调用
after
是否安全?考虑到insert保证不会使其他迭代器失效,并且容器没有
size()
方法,这似乎是安全的,但可能我遗漏了什么

编辑:

我编写了一个小的酷刑测试程序,在没有任何锁定的情况下运行良好:

#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';
}
#包括
#包括
#包括
#包括
使用List=std::forward_List;
使用它=列表::常量迭代器;
void insertAndBranch(列表和列表、It、int-depth)
{
if(深度-->0){
It newIt=list.insert_在(It,depth)之后;
std::thread0([&]{insertAndBranch(list,it,depth);});
std::thread1([&]{insertAndBranch(list,newIt,depth);});
thread0.join();
thread1.join();
}
}
int main()
{
名单;
insertAndBranch(list,list.before_begin(),8);
std::vectorits;
for(It=list.begin();It!=list.end();+It){
把它推回(它);
}
std::vectorthreads;
因为(它:它的){
threads.emplace_back([&]{list.insert_在(it,-1);})之后);
}
用于(标准:螺纹和螺纹:螺纹){
thread.join();
}
用于(int i:列表){
标准::cout
在共享的
std::list
中,多线程调用insert安全吗 同时,如果他们保证不会使用相同的 位置迭代器

不,那是不安全的…(不管怎样)

插入
std::list
将需要访问
上一个节点
下一个节点
到迭代器位置,以及列表的
大小

即使位置彼此相距很远,一个简单的事实是需要恒定时间(C++11)。这意味着每次插入都将更新
std::list::size()


编辑:

在共享的
std::forward_列表中
允许多个线程 如果保证从不调用insert_,则并发调用insert_ 使用相同的位置迭代器

它不安全,不推荐使用。没有STL容器设计有线程安全性


无论如何,让我们假设一些基本的保证,让我们假设一个非常简单的版本的
std::forward_list
insert_-after
修改迭代器指向的节点,这样节点现在指向新插入的节点,而新插入的节点指向下一个节点。所以它只会是“安全的”如果迭代器之间至少有两个节点,并且您的分配器是线程安全的

说明:

Initial Forward_list
A -> B -> C -> D -> E

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


至于我为什么说“至少两个节点远离”,让我们把一个节点的情况拿走:假设两个线程分别想在
C
之后插入
K
,在
D
之后插入
M

Initial Forward_list
A -> B -> C -> D -> E

Insert K after C
A -> B -> C x D x E
           \ / \ /
            K   M
发件人:

当表达式的计算写入内存位置时 另一个计算读取或修改相同的内存位置 据说表达方式是冲突的


当另一个线程写入列表时,即使是读取操作也不是线程安全的(有关详细说明,请参阅);因此:多次写入不是线程安全的:

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


你需要某种锁。句型。< /P>复制的可能副本是旧的,而且事情已经改变。OP没有指定C++标准…C++ 14,我只是添加了一个标签。任何时候你有多个线程,其中一个或多个是共享变量的写入器,那么你需要同步。如果没有,你有一个未定义的数据竞争。行为。事实上,我在代码中使用的是

std::forward_list
,而不是
std::list
。为了简单起见,我用
std::list
写了这个问题,但我现在改了。有趣的是
std::forward_list
没有大小方法,这会改变你的答案吗?在双链接列表中,但在单链列表中,你是正确的e链表唯一需要修改的两个节点是position参数指向的节点和新创建的节点,这两个节点都不会被另一个线程读取或修改。@atb,我已经更新了我的答案,以论证您的评论(请参见插图)。您仍然需要至少两个节点。尽管如此,这并不是一个保证。使用锁或其他具有线程安全列表的库在您的第一个图表中,我认为不会读取D。下一个节点指针将从C获取并提供给K。我无法想象为什么一个高效的实现需要从D读取。@atb,我刚刚通过对话关于这个问题的电子邮件。虽然“标准不能保证它,但他也想不到它会失败”…有了,我认为如果你能实现自己的保证,你就可以走了。你可能是对的,但我想知道为什么在这种情况下。这不是那么简单。例如,如果我们有一个带有一个元素的转发列表,那么:从一个线程读取其数据(
a=l.begin()->d;
),然后在另一个线程中添加一个元素(
l.insert_在(l.begin(),X)之后;
)不应触发任何竞争条件。这是因为两个线程中都没有访问共享数据:第一个线程只读取
begin
begin->d
,第二个线程写入
begin->next
@ShmuelH.AFAIK除非调用const版本,否则不能保证
begin
是线程安全的。
begin()
可以做点什么(如果我