简体   繁体   English

std::async 的非阻塞调用:这个版本有多危险?

[英]non-blocking call of std::async: how is this version dangerous?

Some time ago I was looking for a way to invoke std::async without the need of storing std::future , thus not blocking the execution at the end of the scope.前段时间,我正在寻找一种无需存储std::future即可调用std::async的方法,因此不会在 scope 结束时阻塞执行。 I found this answer which uses a captured std::shared_ptr for an std::future , therefore allowing to make a nonblocking call to std::async .我发现这个答案使用捕获的std::shared_ptr作为std::future ,因此允许对std::async进行非阻塞调用。

Another way of deferring a destructor invocation is to prevent it from to be called at all.另一种延迟析构函数调用的方法是完全阻止它被调用。 This can be achieved with in-place construction with operator new .这可以通过使用operator new的就地构造来实现。

Consider this version that uses a static thread local storage for an in-place constructed std::future<void> :考虑这个使用 static 线程本地存储的版本,用于就地构造的std::future<void>

template <class F>
void call_async(F&& fun) {
    thread_local uint8_t buf[sizeof(std::future<void>)] = {0};
    auto fut = new(buf) std::future<void>();
    *fut = std::async(std::launch::async, [fun]() {
        fun();
    });
}

This version will not produce any heap-allocation related overhead, but it seems very illegal, though I am not sure why in particular.这个版本不会产生任何与堆分配相关的开销,但它似乎非常非法,尽管我不确定具体原因。

I am aware that it is UB to use an object before it has been constructed, which is not the case.我知道在构建之前使用 object 是 UB,但事实并非如此。 I am not sure why not calling delete in this case would resolve in UB (for heap allocation it is not UB).我不确定为什么在这种情况下不调用delete会在 UB 中解决(对于堆分配,它不是 UB)。

Possible problems that I see:我看到的可能问题:

  • calling a constructor on one object multiple times多次在一个 object 上调用构造函数
  • race condition when modifying the state (inner std::promise I suppose)修改 state 时的竞争条件(我想是内部std::promise

https://ideone.com/C44cfe https://ideone.com/C44cfe

UPDATE更新

Constructing an object in the static storage directly (as has mentioned IlCapitano in the comments) will block each time a move assignment is called (shared state will be destroyed blocking the thread which has removed last reference to it).在 static 存储中直接构造一个 object(如评论中提到的 IlCapitano)将在每次调用移动分配时阻塞(共享 state 将被删除的阻塞引用线程)。

Not calling a destructor will case a leak because of not released references to the shared state.不调用析构函数将导致泄漏,因为未发布对共享 state 的引用。

It's undefined behaviour to end the lifetime of a non-trivial object without calling it's destructor, which happens as soon as there is a second call_async invocation.在不调用它的析构函数的情况下结束非平凡 object 的生命周期是未定义的行为,一旦有第二次call_async调用就会发生这种情况。

"heap-allocation related overhead" is a misnomer if the only alternative is undefined behaviour.如果唯一的选择是未定义的行为,则“与堆分配相关的开销”是用词不当。 The future returned by async has to live somewhere . async返回的future必须存在于某个地方

The updated code has defined behaviour: it waits for the previous invocation to be done before launching the next one.更新后的代码已经定义了行为:它在启动下一个调用之前等待上一个调用完成。

Calling std::async and ignoring the result sounds like "fire and forget".调用std::async并忽略结果听起来像是“一劳永逸”。 The simplest way to do that is to not use std::async , but to create a detached thread:最简单的方法是不使用std::async ,而是创建一个分离的线程:

std::thread thr(func, data...);
thr.detach();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM