Sil*_*ler 21 c++ multithreading language-lawyer c++11
我对线程安全方面的要求有点困惑std::promise::set_value().
该标准说:
效果:以原子方式将值r存储在共享状态中并使该状态准备就绪
但是,它也说promise::set_value()只能用于设置一次值.如果多次调用,std::future_error则抛出a.因此,您只能设置一次承诺的值.
实际上,几乎每个教程,在线代码示例或实际用例都std::promise涉及两个线程之间的通信通道,其中一个线程调用std::future::get(),另一个线程调用std::promise::set_value().
我从来没有见过多个线程可能调用的用例std::promise::set_value(),即使他们这样做,除了一个std::future_error之外的所有线程都会引发异常.
那么为什么调用的标准命令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::promise::set_value()必须实际上是一个原子操作,好像一个调用std::promise::set_value()是完全在互斥锁下执行的?除非有多个原因或用例std::promise::set_value()同时调用多个线程,否则我认为没有理由存在此要求.我想不出这样一个用例,因此这个问题.
如果它不是原子存储,那么两个线程可以同时调用promise::set_value,这将执行以下操作:
通过使该序列原子,第一个线程来执行(1)获取一路走过来(3),以及任何其他线程调用promise::set_value在同一时间将在(1)失败并养future_error有promise_already_satisfied。
如果没有原子性,则两个线程可能会存储它们的值,然后一个线程会成功地将状态标记为就绪,而另一个线程会引发异常,即相同的结果,只是线程中看到异常的值可能是相同的。通过了。
在许多情况下,哪个线程“获胜”可能无关紧要,但是当它重要时,如果没有原子性保证,您将需要在promise::set_value调用周围包装另一个互斥体。其他方法(例如比较和交换)将不起作用,因为您无法检查未来(除非是shared_future)来查看您的价值是否赢了。
当哪个线程“获胜”无关紧要时,您可以为每个线程分配自己的未来,并std::experimental::when_any用来收集碰巧可用的第一个结果。
经过一些历史研究后编辑:
尽管以上内容(使用同一个Promise对象的两个线程)似乎不是一个很好的用例,但肯定future是C语言引入的当代论文之一:N2744。本文提出了几个用例,这些用例具有冲突的线程调用set_value,我在这里引用它们:
其次,考虑用例,其中并行执行两个或多个异步操作并“竞争”以满足承诺。一些示例包括:
- 结合等待定时器来执行一系列网络操作(例如,请求网页)。
- 可以从多个服务器检索值。为了实现冗余,将尝试所有服务器,但仅需要获得第一个值。
在两个示例中,要完成的第一个异步操作都是满足承诺的操作。由于任何一个操作都可能第二秒完成,因此必须编写这两个操作的代码以期望调用
set_value()可能失败。
我从未见过多个线程可能调用 std::promise::set_value() 的用例,即使它们调用了,除了一个线程之外的所有线程都会导致抛出 std::future_error 异常。
你错过了 Promise 和 Future 的整个概念。
通常,我们有一对承诺和未来。promise 是你推送异步结果或异常的对象,future 是你拉异步结果或异常的对象。
在大多数情况下,future 和 promise 对不在同一个线程上,(否则我们将使用一个简单的指针)。因此,您可以将承诺传递给某个线程、线程池或某个第三方库异步函数,并从那里设置结果,并将结果拉入调用者线程。
设置结果std::promise::set_value必须是原子的,不是因为许多承诺设置了结果,而是因为驻留在另一个线程上的对象(未来)必须读取结果,并且非原子地执行是未定义的行为,因此设置值并拉动它(通过调用std::future::get或std::future::then)必须以原子方式发生
请记住,每个 future 和 promise 都有一个共享状态,设置一个线程的结果更新共享状态,并从共享状态读取结果。就像 C++ 中的每个共享状态/内存一样,当它从多个线程完成时,更新/读取必须在锁定下发生。否则它是未定义的行为。
| 归档时间: |
|
| 查看次数: |
4084 次 |
| 最近记录: |