用自己的版本替换std :: async,但std :: promise应该在哪里?
我使用的是vc2011,事实证明std :: async(std :: launch :: async,...)是有点bug的(有时它不会产生新的线程并且并行地运行它们,而是重用线程和一个接一个地运行任务)。 当我进行昂贵的网络通话时,这太慢了。 所以我想我会写我自己的异步功能。 我陷入困境,但应该在哪里::承诺住? 在1)线程函数中,2)异步函数或3)调用者函数。
码:
#include <future>
#include <thread>
#include <iostream>
#include <string>
#include <vector>
std::string thFun() {
throw std::exception("bang!");
return "val";
}
std::future<std::string> myasync(std::promise<std::string>& prms) {
//std::future<std::string> myasync() {
//std::promise<std::string> prms; //needs to outlive thread. How?
std::future<std::string> fut = prms.get_future();
std::thread th([&](){
//std::promise<std::string> prms; //need to return a future before...
try {
std::string val = thFun();
prms.set_value(val);
} catch(...) {
prms.set_exception(std::current_exception());
}
});
th.detach();
return fut;
}
int main() {
std::promise<std::string> prms; //I really want the promise hidden iway in the myasync func and not live here in caller code but the promise needs to outlive myasync and live as long as the thread. How do I do this?
auto fut = myasync(prms);
//auto fut = myasync(); //Exception: future already retrieved
try {
auto res = fut.get();
std::cout << "Result: " << res << std::endl;
} catch(const std::exception& exc) {
std::cout << "Exception: " << exc.what() << std::endl;
}
}
我似乎无法摆脱std :: promise需要超越异步函数(并且与线程一样长)的事实,所以promise不能作为异步func中的局部变量。 但std :: promise不应该存在于调用者代码中,因为调用者只需要知道未来。 而且我不知道如何让这个承诺活在线程函数中,因为异步需要在它调用线程func之前返回未来。 我在这个问题上摸不着头脑。
任何人有任何想法?
编辑:我在这里强调这一点,因为最重要的评论有点误导。 虽然允许std :: asycn的默认模式为dererred模式,但当明确设置std :: launch :: async的启动策略时,它必须表现为“仿佛”线程一样产生并立即运行(请参阅en中的措词.cppreference.com / W / CPP /线程/异步)。 查看pastebin.com/5dWCjjNY中的示例,其中这不是vs20011中看到的行为。 该解决方案效果很好,加快了我的真实世界应用程序的10倍。
编辑2:MS修复了这个错误。 更多信息在这里:https://connect.microsoft.com/VisualStudio/feedback/details/735731/std-async-std-launch-async-does-not-behave-as-std-thread
这是一个解决方案:
future<string> myasync()
{
auto prms = make_shared<promise<string>> ();
future<string> fut = prms->get_future();
thread th([=](){
try {
string val = thFun();
// ...
prms->set_value(val);
} catch(...) {
prms->set_exception(current_exception());
}
});
th.detach();
return fut;
}
在堆上分配诺言,然后通过value [=]
传递一个shared_ptr给lambda。
您需要将承诺转移到新线程中。 Andrew Tomazos的回答是通过创建具有共享所有权的std::promise
来实现的,这样两个线程都可以拥有该承诺,并且当前一个线程从当前范围返回时,只有新线程拥有承诺,即拥有权已转移。 但std::promise
是可移动的,所以应该可以直接将它移动到新线程中,除了捕获它的“明显”解决方案不起作用,因为lambda不能通过移动捕获,只能通过复制(或通过参考,这将不会工作,因为你会得到一个悬而未决的参考。)
但是, std::thread
支持将rvalue对象传递给新线程的启动函数。 所以你可以声明lambda采用一个std::promise
参数的值,即将promise
传递给lambda而不是捕获它,然后将promise转换为std::thread
一个参数,例如
std::future<std::string> myasync() {
std::promise<std::string> prms;
std::future<std::string> fut = prms.get_future();
std::thread th([&](std::promise<std::string> p){
try {
std::string val = thFun();
p.set_value(val);
} catch(...) {
p.set_exception(std::current_exception());
}
}, std::move(prms));
th.detach();
return fut;
}
这将承诺移动到std::thread
对象中,然后将它(在新线程的上下文中)移动到lambda的参数p
。
上一篇: Replacing std::async with own version but where should std::promise live?
下一篇: the behavior of std::async with std::launch::async policy