【发布时间】:2018-09-13 09:15:21
【问题描述】:
C++ coroutines TS (2017) 中有一个可等待对象的示例。
template <class Rep, class Period>
auto operator co_await(std::chrono::duration<Rep, Period> d) {
struct awaiter {
std::chrono::system_clock::duration duration;
...
awaiter(std::chrono::system_clock::duration d) : duration(d){}
bool await_ready() const { return duration.count() <= 0; }
void await_resume() {}
void await_suspend(std::experimental::coroutine_handle<> h){...}
};
return awaiter{d};
}
using namespace std::chrono;
my_future<int> h();
my_future<void> g() {
std::cout << "just about go to sleep...\n";
co_await 10ms;
std::cout << "resumed\n";
co_await h();
}
像典型的 StackOverflow 问题一样,它不会编译。悄悄骂了一阵子,我决定把它变成一个【MCVE】——用来学习。下面的代码在启用 /await 的 VC++17 上编译和运行。我认为它可能大致符合 TS 作者的意图。唉,它使用了一个分离的线程。通过join 或future::get 或signal_all_at_thread_exit() 或...
例如,不能将连接添加到awaiter 的析构函数。在生成的线程中,h.resume() 导致等待者对象被移动到生成的线程中,并在那里调用其(默认)构造函数。所以析构函数在与构造函数不同的线程中被调用。
问题,除了“这是 TS 的意图吗?”之外,是 “是否可以以合理经济的方式进行改进,以处理悬空线程?”(如果是的话怎么办?)
#include <experimental/coroutine>
#include <future>
#include <thread>
namespace xtd = std::experimental;
template <class Rep, class Period>
auto operator co_await(std::chrono::duration<Rep, Period> dur) {
struct awaiter {
using clock = std::chrono::high_resolution_clock;
clock::time_point resume_time;
awaiter(clock::duration dur) : resume_time(clock::now()+dur) {}
bool await_ready() { return resume_time <= clock::now(); }
void await_suspend(xtd::coroutine_handle<> h) {
std::thread([=]() {
std::this_thread::sleep_until(resume_time);
h.resume(); // destructs the obj, which has been std::move()'d
}).detach(); // Detach scares me.
}
void await_resume() {}
};
return awaiter{ dur };
}
using namespace std::chrono;
std::future<int> g() {
co_await 4000ms;
co_return 86;
}
template<typename R>
bool is_ready(std::future<R> const& f)
{ return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready; }
int main() {
using std::cout;
auto gg = g();
cout << "Doing stuff in main, while coroutine is suspended...\n";
std::this_thread::sleep_for(1000ms);
if (!is_ready(gg)) {
cout << "La lala, lala, lala...\n";
std::this_thread::sleep_for(1500ms);
}
cout << "Whew! Done. Getting co_return now...\n";
auto ret = gg.get();
cout << "coroutine resumed and co_returned " << ret << '\n';
system("pause");
return ret;
}
【问题讨论】:
标签: c++ multithreading coroutine