【问题标题】:C++ thread safe vector insertionC++ 线程安全向量插入
【发布时间】:2020-06-28 05:12:39
【问题描述】:

多个线程处理数据,最后它们都在 RESULT 向量中插入/追加结果。
我想确保一次只有一个线程在 RESULT 向量中插入元素。
为此,我使用了 mutex 和 lock_guard(),但它们有时会给出无效访问错误,有时它们不会...
这是我的同步代码

void insert_output() {
    mutex m;
    lock_guard<mutex> lockGuard(m);
    Resources::results.insert(Resources::results.begin(), output.begin(), output.end());
}

我是不是做错了什么...
还有什么更好的方法来同步这个过程?

【问题讨论】:

  • 另外,附注:在std::vector always 的开头 插入需要复制/移动整个向量。这将很快花费很长时间。您应该只在std::vector 的end 处插入。如果需要在开头插入,请使用std::deque 或std::list 或在末尾反向插入,然后将向量反向。

标签: c++ multithreading c++11 c++14 c++17


【解决方案1】:

您需要将互斥锁设置为一个实例, 它为每个实例生成一个锁。

mutex m;
void insert_output() {
    lock_guard<mutex> lockGuard(m);
    Resources::results.insert(Resources::results.begin(), output.begin(), output.end());
}

【讨论】:

  • 好的,这很好,但下一个问题是我应该将 mutex m 声明为静态变量,因为正如您所说的“将互斥锁设为一个实例”以避免“每个实例锁定”?
【解决方案2】:

向量必须只有 一个 互斥体。所以你应该在向量旁边添加互斥锁,例如在Resources 中作为results_mutex。如果results 是静态成员,那么互斥锁也应该是静态成员(因此向量只有一个互斥锁)。

然后,您还必须在访问向量的所有操作上锁定互斥锁,这些操作可能与调用insert_output 并行执行,而不是仅在插入操作上。

在您当前的代码中,您在每次调用时都会创建一个新的互斥体,这完全没有意义。

【讨论】:

  • 插入是任何线程使用结果向量的唯一地方...所有线程都有自己的输出结果来存储临时结果,一旦完成,它们会将输出插入主共享结果向量中。所以,我要确保结果向量只在一个地方使用......我想问我是否应该将该互斥锁声明为静态变量,以确保它在所有线程之间共享,就像我对主要结果向量所做的那样.
  • @xyzxyz 如果是这种情况,您可以简单地将m 声明为static 或在函数外部声明为全局并完成它,是的,但这不是一个干净的解决方案。它之所以起作用,只是因为results 是一个静态变量,如果它是一个非静态成员,它将无法正常工作。如果互斥锁直接与它所保护的对象配对,也更容易推理代码。
  • 嘿,我应该把 lock_guard 也放在资源类中...作为静态成员吗​​?
  • @xyzxyz 不,lock_guard 始终应该是您打算访问受保护对象的块中的自动变量(并在实际访问之前声明)。如果有多个这样的地方,你会使用多个lock_guards,但仍然只有一个互斥体。
【解决方案3】:

稍微增强规范,这变得非常简单。如果您有n 线程,每个线程都创建m 结果,只需使用n*m 元素创建您的向量,并让每个线程将其结果复制到适当的子范围中。所以线程j 通过results[j*m + m - 1] 将其结果复制到results[j*m]。不需要同步,因为没有线程同时修改同一个元素。

更一般地说,如果您提前知道每个线程将创建多少个结果,您可以适当地平铺结果向量,以免写入冲突。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多