根据这个最新的C ++ TS:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4628.pdf,基于对C#async / await语言支持的理解,我想知道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的调度程序,可以在线程上执行协同程序启动 root 协同程序。代码可以在这个github repo中找到:https://github.com/llint/Awaitable
Awaitable是一种可组合的方式,它在嵌套级别维护正确的调用排序,并且它具有原始的让步,定时等待和从其他地方设置就绪,并且可以从中导出非常复杂的使用模式(例如无限循环)只有在某些事件发生时才被唤醒的协程,编程模型紧跟C#Task async / await模式。请随时提供反馈。
答案 0 :(得分:11)
相反!
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
时,程序会询问是否应该暂停协程(如果await_ready
返回false),如果是这样的话 - 你会得到一个协程句柄,你可以在其中做你喜欢的事。
你可以将协程句柄传递给一个简单的std::thread
- 你的拥有创建线程将恢复协程。
您可以将协程句柄附加到OVERLAPPED
的派生类中,并在异步IO完成时恢复协程。
正如您所看到的 - 您可以通过管理await_suspend
中传递的协程句柄来控制协程暂停和恢复的位置和时间。没有“默认调度程序” - 你如何实现等待类型将决定如何对协程进行schedueled。
那么,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();
}
那么为什么在常规std::thread
中启动协同程序时会看到win32线程池线程?那是因为它不是协程。 std::async
在幕后调用concurrency::create_task
。默认情况下,在win32线程池下启动concurrency::task
。毕竟,std::async
的整个目的是在另一个线程中启动callable。