Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.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++ 以线程安全的方式设置具有回调的类成员_C++_Multithreading_Callback_C++14 - Fatal编程技术网

C++ 以线程安全的方式设置具有回调的类成员

C++ 以线程安全的方式设置具有回调的类成员,c++,multithreading,callback,c++14,C++,Multithreading,Callback,C++14,我有一个类,它允许通过一个昂贵的计算来设置一个值,我们可以假设计算是在其他线程中异步进行的。该类还有一个获取当前值的方法,如果需要,可以同步执行该计算: class Example { public: Example() : x_(0) {} void Set(int x) { SomeOtherFunc(x, callback_, false); } void Finished(int y) { x_ = y; }

我有一个类,它允许通过一个昂贵的计算来设置一个值,我们可以假设计算是在其他线程中异步进行的。该类还有一个获取当前值的方法,如果需要,可以同步执行该计算:

class Example {
  public:
    Example() : x_(0) {}
    void Set(int x) {
        SomeOtherFunc(x, callback_, false);
    }

    void Finished(int y) {
      x_ = y;
    }

    const int Get() {
      if (!x_) {
          SomeOtherFunc(1, callback_, true);
      }
      return x_;
    }

  private:
    int x_;
    std::function<void(int)> callback_ =
        std::bind(&Example::Finished, this, std::placeholders::_1);
};
我的问题是:

  • 示例
    类是线程安全的吗?如果没有,如何做到这一点?看起来锁会起作用,但可能有点过头了
  • 由于
    SomeOtherFunc
    中的工作非常昂贵,因此我只想调用该函数一次。在类中设置一个标志,该标志在每次调用时都设置为true,并在调用之前进行检查,这似乎是合理的(但它是线程安全的吗?)。问题是,如果我调用
    Set
    然后紧接着调用
    Get
    ,我希望
    Get
    返回
    x的“最终”值。我如何确保这一点?也就是说,如果设置了该标志,如何使
    Get
    以线程安全的方式等待回调完成

  • 你可能想用一个

    类模板std::future提供了一种访问异步操作结果的机制: 异步操作(通过std::async、std::packaged_任务或std::promise创建)可以向该异步操作的创建者提供std::future对象。 然后,异步操作的创建者可以使用各种方法来查询、等待或从std::future中提取值。如果异步操作尚未提供值,则这些方法可能会阻塞。 当异步操作准备好向创建者发送结果时,它可以通过修改链接到创建者的std::future的共享状态(例如std::promise::set_值)来实现


    您的
    示例
    类不是线程安全的,因为可以同时修改整数
    x
    ,这可能导致未定义的行为(数据竞争)。 此外,由于您在
    Get()
    中有一个竞争条件,您可能会多次执行昂贵的计算,方法是选中
    x
    ,然后调用将设置它的函数。
    您需要一种机制来保证在完全线程安全的情况下,
    x
    的值将精确计算一次(
    Set()
    Get()
    可以同时调用)

    标准库提供了一种机制来精确地解决这个问题,
    call_once()
    ,旨在实现一次性事件,同时在线程之间正确地同步共享数据。 您可以这样使用它:

    #include <mutex>
    
    class Example {
      public:
        ...
    
        void Set(int x) {
            std::call_once(flag, SomeOtherFunc, x, callback_, false);
        }
    
        ...
    
        const int Get() {
            std::call_once(flag, SomeOtherFunc, 1, callback_, true);
            return x_;
        }
    
      private:
        std::once_flag flag;
        ...
    };
    
    #包括
    课例{
    公众:
    ...
    无效集(int x){
    std::调用一次(flag,SomeOtherFunc,x,callback,false);
    }
    ...
    常量int Get(){
    std::call_once(flag,SomeOtherFunc,1,callback_,true);
    返回x;
    }
    私人:
    std::once_标志;
    ...
    };
    
    这也可以处理这样的情况,
    Get()
    在回调处理结果时必须等待

    请注意,您不再能够(也不必)在
    Get()
    中检查
    x
    的值,因为这将构成数据竞争(另一个线程可能同时更新
    x


    还要注意,直接调用回调函数
    Finished()
    不是线程安全的。可能最好将其移动到
    private:
    部分。

    如果当前正在进行调用,那么call\u once将在
    Get()
    中阻塞?这正是我想要的。@SteveD是的,
    call\u once
    保证了这一点。。如果
    SomeOtherFunc()
    正在进行计算,则对
    Get()
    (和
    Set()
    )的所有(并发)调用都将被阻止,无论是由
    Get()
    还是
    Set()
    启动。再次感谢,这正是我所需要的
    Example e1, e2;
    e1.Set(5);
    // "10 2"
    std::cout << e1.Get() << " " << e2.Get();
    
    Example e;
    e.Set(10); // Takes a while
    e.Get();   // Need the value now
    
    #include <mutex>
    
    class Example {
      public:
        ...
    
        void Set(int x) {
            std::call_once(flag, SomeOtherFunc, x, callback_, false);
        }
    
        ...
    
        const int Get() {
            std::call_once(flag, SomeOtherFunc, 1, callback_, true);
            return x_;
        }
    
      private:
        std::once_flag flag;
        ...
    };