C ++ 1z协程线程上下文和协程调度
根据最新的C ++ TS:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4628.pdf,基于对C#异步/等待语言支持的理解,我想知道什么是C ++协程的“执行上下文”(从C#借用的术语)?
我在Visual C ++ 2017 RC中的简单测试代码揭示了协程似乎总是在一个线程池线程上执行,并且很少控制应用程序开发人员在其上执行协程的线程上下文 - 例如,应用程序是否可以强制所有协程与编译器生成的状态机代码)只能在主线程上执行,而不涉及任何线程池线程?
在C#中,SynchronizationContext是一种指定所有协程“半部”(编译器生成的状态机代码)将被发布和执行的“上下文”的方式,如下所示:https://blogs.msdn.microsoft.com / pfxteam / 2012/01/20 / await-synchronizationcontext-and-console-apps /,而Visual C ++ 2017 RC中的当前协程实现似乎总是依赖并发运行时,默认情况下执行生成的状态机代码线程池线程。 是否有类似的同步上下文概念,用户应用程序可以使用它来将协程执行绑定到特定的线程?
另外,在Visual C ++ 2017 RC中实现的协程的当前默认“调度程序”行为是什么? 即1)等待条件如何精确指定? 2)当等待条件满足时,谁调用暂停协程的“下半部分”?
我关于C#中任务调度的(天真的)猜测是C#完全通过任务延续“实现”了等待条件 - 一个等待条件由TaskCompletionSource拥有的任务合成,任何需要等待的代码逻辑将被链接为所以如果满足等待条件,例如,如果从低级网络处理程序接收到完整消息,则执行TaskCompletionSource.SetValue,其将底层任务转换为完成状态,从而有效地允许链式继续逻辑开始执行(将任务放入从前一个创建状态开始的就绪状态/列表) - 在C ++协程中,我推测std :: future和std :: promise将用作类似的机制(std :: future是任务,而std :: promise就是TaskCompletionSource,而且它的用法也非常相似!) - C ++协程调度程序(如果有的话)也依赖于某种类似的机制来执行该行为?
[编辑]:在做了一些进一步的研究后,我能够编写一个非常简单但非常强大的awaitable抽象,支持单线程和协作式多任务处理,并且具有一个简单的基于thread_local的调度器,它可以在线程上执行协程开始。 代码可以从这个github仓库中找到:https://github.com/llint/Awaitable
Awaitable组合的方式是它可以在嵌套级别维护正确的调用顺序,并且它具有原始的产生式,定时等待以及从其他位置准备就绪的特性,并且可以从中派生出非常复杂的使用模式(例如无限循环协程在某些事件发生时醒来),编程模型紧跟C#Task基于异步/等待模式。 请随时提供您的反馈。
相反!
C ++协程完全是关于控制。 这里的关键是
void await_suspend(std::experimental::coroutine_handle<> handle)
函数。
evey co_await
预计等待类型。 简而言之,等待类型是提供这三种功能的类型:
bool await_ready()
- 程序应该停止协程的执行吗? void await_suspend(handle)
- 程序向您传递该协程框架的延续上下文。 如果你激活了句柄(例如,通过调用operator ()
,句柄提供 - 当前线程立即恢复协程)。 T await_resume()
- 告诉恢复协程时要恢复协程的线程以及从co_await
返回的co_await
。 所以当你在等待类型上调用co_await
时,如果协程被暂停(如果await_ready
返回false),程序会询问等待状态,如果是这样 - 你会得到一个协程句柄,你可以在其中执行任何你喜欢的操作。
例如,您可以将协程句柄传递给线程池。 在这种情况下,线程池线程将恢复协程。
您可以将协程句柄传递给一个简单的std::thread
- 您自己的创建线程将恢复协程。
您可以将协程句柄附加到派生类OVERLAPPED
并在异步IO完成时恢复协程。
正如你所看到的 - 你可以通过管理在await_suspend
传递的协程句柄来控制协程暂停和恢复的位置和await_suspend
。 没有“默认调度程序” - 如何实现您的等待类型将决定如何调度协程。
那么,VC ++会发生什么? 不幸的是, std::future
仍然没有then
功能,所以你不能在协同程序句柄传递到std::future
。 如果您在std::future
上等待 - 程序将只打开一个新线程。 看看future
头文件给出的源代码:
template<class _Ty>
void await_suspend(future<_Ty>& _Fut,
experimental::coroutine_handle<> _ResumeCb)
{ // change to .then when future gets .then
thread _WaitingThread([&_Fut, _ResumeCb]{
_Fut.wait();
_ResumeCb();
});
_WaitingThread.detach();
}
那么,为什么你看到一个win32线程池线程,如果协程是在一个普通的std::thread
呢? 那是因为它不是协程。 std::async
在幕后调用concurrency::create_task
。 一个concurrency::task
默认在win32 concurrency::task
池下启动。 毕竟, std::async
的全部目的是在另一个线程中启动可调用。
上一篇: C++1z coroutine threading context and coroutine scheduling