【问题标题】:Grabbing a lock that's valid only once in C++获取在 C++ 中仅有效一次的锁
【发布时间】:2014-10-28 13:27:52
【问题描述】:

我正在使用 C++ 11,其中有 4 个线程并行插入到并发队列中。我知道线程何时完成处理,即队列的预期最终大小。

现在,我想对队列的内容执行最终聚合操作,该操作应该严格只执行一次。例如,假设我想汇总这些值并将其发布到外部服务。

如何获得只有效一次的锁?我不能使用简单的互斥锁,因为那不能保证我只有一次要求。

伪代码:

// inside a thread
enqueue items to concurrent_queue

if(concurrent_queue.size() == EXPECTED_SIZE) {
    // do something ONLY once
}

【问题讨论】:

  • std::call_once 怎么样?
  • 你可能想要使用锁存器或信号量,所有线程在完成处理后都尝试获取锁,一旦所有线程都进行了调用,只有一个线程被释放.

标签: c++ multithreading c++11 concurrency locking


【解决方案1】:

一个简单的解决方案。

if(concurrent_queue.size() == EXPECTED_SIZE) {
    // do something ONLY once
    static bool doItOnce = DoItOnceOnly();
}

【讨论】:

  • 这确实要求所有并发线程都运行相同的函数。如果没有,您可以重构。请注意,缺点是每个程序执行一次,而不是每个操作一次或其他。
  • @Yakk,没错,只是让所有不同的线程调用具有static 的同一个额外函数。
【解决方案2】:

如果您不担心操作失败的可能性,您可以简单地使用与操作关联的atomic<bool>。所有线程都会尝试使用compare_exchange_strong 将标志从false 更改为true,但只有一个会成功:

// inside a thread
enqueue items to concurrent_queue

if(concurrent_queue.size() == EXPECTED_SIZE) {
    std::atomic<bool>& flag = retrieve_the_associated_bool();
    bool expected = false;
    if (flag.compare_exchange_strong(expected, true)) {
      // do something
    }
}

如果操作可能失败,您应该使用std::call_once 和与操作关联的std::once_flag。其他线程会在一个尝试“做某事”时等待,然后每个线程依次尝试直到成功:

// inside a thread
enqueue items to concurrent_queue

if(concurrent_queue.size() == EXPECTED_SIZE) {
    std::once_flag& flag = retrieve_the_associated_once_flag();
    std::call_once(flag, []{
        // do something
    });
    // do something ONLY once
}

【讨论】:

    【解决方案3】:

    检查单例实现的方法。最基本的:

    bool executed = false;
    void Execute() 
    {
        Lock lock;      // scope-based lock, released automatically when the function returns
        if (executed == false) 
        {
           executed = true;
           //.. do computation
        }
    }
    

    还检查具有原子变量和双重检查锁定的解决方案以获得更好的性能: http://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/

    【讨论】:

    • 是的,布尔值应该是全局或类成员。可能在 concurrent_queue 声明附近。
    猜你喜欢
    • 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
    相关资源
    最近更新 更多