C++ std::PROMICE set_值和线程安全

C++ std::PROMICE set_值和线程安全,c++,multithreading,c++11,language-lawyer,C++,Multithreading,C++11,Language Lawyer,我对std::promise::set_value()上的线程安全要求有点困惑 委员会: 效果:以原子方式将值r存储在共享状态中,并使 那个州准备好了吗 但是,它还指出,promise::set_value()只能用于设置一次值。如果多次调用,将抛出std::future\u错误。因此,您只能设置承诺的值一次 实际上,几乎每个教程、在线代码示例或std::promise的实际用例都涉及两个线程之间的通信通道,其中一个线程调用std::future::get(),另一个线程调用std::promi

我对
std::promise::set_value()
上的线程安全要求有点困惑

委员会:

效果:以原子方式将值r存储在共享状态中,并使 那个州准备好了吗

但是,它还指出,
promise::set_value()
只能用于设置一次值。如果多次调用,将抛出
std::future\u错误。因此,您只能设置承诺的值一次

实际上,几乎每个教程、在线代码示例或
std::promise
的实际用例都涉及两个线程之间的通信通道,其中一个线程调用
std::future::get()
,另一个线程调用
std::promise::set_value()

我从未见过多个线程调用
std::promise::set_value()
的用例,即使它们调用了,除了一个线程之外,所有线程都会引发
std::future_错误
异常

那么,为什么调用
std::promise::set_value()
的标准指令是原子的呢?从多个线程同时调用
std::promise::set_value()
的用例是什么


编辑:

因为这里投票最多的答案并没有真正回答我的问题,我想我问的是不清楚的。因此,澄清一下:我知道什么是未来和承诺,以及它们是如何运作的。我的问题是,具体来说,为什么标准坚持
std::promise::set_value()
必须是原子的?这是一个比“为什么对
promise::set_value()
的调用和对
future::get()
的调用之间不存在竞争”更微妙的问题

事实上,这里的许多答案(错误地)回答说,原因是如果
std::promise::set_value()
不是原子的,那么
std::future::get()
可能会导致竞争条件。但事实并非如此

避免竞态条件的唯一要求是
std::promise::set_value()
必须与
std::future::get()
-换句话说,必须保证当
std::future::wait()
返回时,
std::promise::set_value()
已完成

这与
std::promise::set_value()
本身是否为原子完全正交。在使用条件变量的典型实现中,
std::future::get()/wait()
将等待条件变量。然后,
std::promise::set_value()
可以非原子地执行任意复杂的计算来设置实际值。然后它将通知共享条件变量(意味着具有释放语义的内存围栏),并且
std::future::get()
将唤醒并安全地读取结果

因此,
std::promise::set_value()
本身不需要是原子的,以避免这里的竞争条件-它只需要满足与
std::future::get()
的发生之前关系

这样,我的问题是:为什么C++标准坚持<代码> STD::承诺::StIOValueMe()/Cuffe必须是原子操作,就像调用<代码> STD:::承诺::SETIVALUE()/CUT>完全在互斥锁下执行?我看不出这个需求存在的原因,除非多个线程同时调用

std::promise::set_value()
有某种原因或用例。我想不出这样的用例,所以这个问题

我从未见过多个线程可以调用的用例 std::promise::set_value(),即使他们这样做了,除了一个之外,所有人都会这么做 导致引发std::future_错误异常

你错过了承诺和未来的全部概念

通常,我们有一对承诺和未来。承诺是你推的对象,异步结果或异常,未来是你拉的对象,异步结果或异常

在大多数情况下,future和promise对不在同一个线程上(否则我们将使用一个简单的指针)。因此,您可以将承诺传递给某个线程、线程池或某个第三个库异步函数,并从那里设置结果,然后在调用线程中提取结果

使用
std::promise::set_value
设置结果必须是原子的,这不是因为许多承诺设置了结果,而是因为驻留在另一个线程上的对象(未来)必须读取结果,并且以非原子方式执行该操作是未定义的行为,因此设置值并拖动它(通过调用
std::future::get
std::future::then
)必须以原子方式进行


记住,每个未来和承诺都有一个强的共享状态,从一个线程中设置结果更新共享状态,并从共享状态读取结果。就像C++中的每个共享状态/内存一样,当从多个线程完成时,更新/读取必须在锁下发生。否则它是未定义的行为。如果它不是原子存储,那么两个线程可以同时调用

promise::set_value
,执行以下操作:

  • 检查未来是否未准备就绪(即有存储值或异常)
  • 存储值
    • 将状态标记为就绪
    • 释放共享状态上的任何阻塞,使其准备就绪
  • 通过使该序列原子化,要执行的第一个线程(1)将一直执行到(3),同时调用
    promise::set_value
    的任何其他线程将在(1)处失败,并在
    promise_已经满足的情况下引发
    未来_错误

    如果没有原子性,两个线程可能存储它们的值,然后一个线程将成功地将状态标记为就绪,另一个线程将引发异常,即相同的结果,,除了它可能是v
    void thread1()
    {
        // do something. Maybe read from disk, or perform computation to populate value
        v = value;
        flag = true;
    }
    
    void thread2()
    {
        if(flag)
        {
            v2 = v;//Here we have a read problem.
        }
    }