关于新的async
/await
关键字和C# 4.5中的Task
类的可定制性,我有一个问题。
首先,了解我的问题的一些背景:我正在开发一个具有以下设计的框架:
Update()
函数,它枚举列表并查看某些“事物”是否需要执行,并这样做。基本上就像一个大的螺纹脱落器。为了简化事情,让我们假设“要做的事情”是在“完成”时返回布尔true
的函数(不应该被称为next ),而false
则是当sheduler应该再次调用它们的下一个更新时返回它们的函数。Update()
-函数中迭代几百件事情的大循环。举个例子:
Future f1, f2;
bool SomeThingToDo() // returns true when "finished"
{
if (f1 == null)
f1 = Remote1.CallF1();
else if (f1.IsComplete && f2 == null)
f2 = Remote2.CallF2();
else if (f2 != null && f2.IsComplete)
return true;
return false;
}
现在,这一切听起来都令人敬畏,就像async
和await
of C# 5.0一样,在这里可以帮助我。我还没有百分之百完全理解它在引擎盖下做了什么(有什么好的参考资料吗?),但是当我从我看过的几次演讲中得到它时,它用这个非常简单的代码做了我想做的事情:
async Task SomeThingToDo() // returning task is completed when this is finished.
{
await Remote1.CallF1();
await Remote2.CallF2();
}
但是我想不出如何编写我的Update()
函数来实现这样的事情。async
和await
似乎想使用Task
类,而Task
类又似乎需要真正的线程?
到目前为止,我最接近的“解决方案”:
第一个线程(运行SomeThingToDo
)只调用它们的函数一次,并将返回的任务存储在每个Update()
上,测试任务是否完成。
Remote1.CallF1
返回一个带有空操作作为构造函数参数的新任务,并记住返回的任务。当F1实际完成时,它调用任务上的RunSynchronously()
将其标记为已完成。
在我看来,这是对任务系统的歪曲。此外,它在两个线程之间创建共享内存(任务的IsComplete
布尔值),如果可能的话,我希望用我们的远程消息处理系统来替换它们。
最后,它不能解决我的问题,因为它不适用于上面类似等待的SomeThingToDo
实现。异步函数返回的自动生成的任务对象似乎立即完成了吗?
最后我的问题是:
Task<T>
Task
而不使用任何与“阻塞”和“线程”相关的东西吗?async
和await
时,有什么好的参考吗?发布于 2013-06-03 06:58:50
我还没有百分之百完全理解它在引擎盖下的作用--有什么好的参考资料吗?
回到我们设计Mads功能的时候,Stephen和我为MSDN杂志写了几篇不同级别的文章。链接在这里:
http://blogs.msdn.com/b/ericlippert/archive/2011/10/03/async-articles.aspx
从我的文章开始,然后是麦德斯的,然后是斯蒂芬的。
异步函数返回的自动生成的任务对象似乎立即完成了吗?
不,它们是在方法主体中的代码返回或抛出时完成的,与任何其他代码相同。
我可以连接到异步/等待使用我自己的实现而不是
Task<T>
吗?
包含await
的方法必须返回void
、Task
或Task<T>
。但是,等待的表达式可以返回任何类型,只要您可以在其上调用GetAwaiter()
。这不一定是Task
。
如果这是不可能的,我是否可以在没有任何与“阻塞”和“线程”相关的情况下使用Task?
绝对一点儿没错。Task
只是代表将来将要完成的工作。虽然这项工作通常是在另一个线程上完成的,但没有要求。
发布于 2013-06-03 06:52:56
我能连接到异步/等待使用我自己的实现而不是任务吗?
是。
如果这是不可能的,我是否可以在没有任何与“阻塞”和“线程”相关的情况下使用Task?
是。
有什么好的参考吗?当我写异步并等待时会发生什么?
是。
我会劝你不要问“是”或“否”的问题。你可能不只是想要“是”或“不是”。
异步和等待似乎想要使用任务类,而任务类又似乎需要真正的线程?
不,那不是真的。Task
表示将来某个时候可以完成的事情,可能会有一个结果。它有时是另一个线程中某些计算的结果,但不需要这样做。它可以是未来某一时刻发生的任何事情。例如,它可能是IO操作的结果。
Remote1.CallF1
返回一个带有空操作作为构造函数参数的新任务,并记住返回的任务。当F1实际完成时,它调用任务上的RunSynchronously()
将其标记为已完成。
所以,这里您缺少的是TaskCompletionSource
类。有了那块缺失的拼图,应该有很多地方适合。您可以创建TCS对象,将Task
从它的Task
属性传递到to...whomever周围,然后使用SetResult
属性来表示它已经完成。这样做不会导致创建任何额外的线程,也不会使用线程池。
请注意,如果您没有结果,只想要一个Task
而不是Task<T>
,那么只需使用TaskCompletionSource<bool>
或类似的东西,然后使用SetResult(false)
或其他合适的东西。通过将Task<bool>
转换为Task
,您可以将该实现隐藏在公共API中。
这也应该提供你问的前两个问题的“如何”变化,而不是你问的“我可以”的版本。您可以使用一个TaskCompletionSource
来生成一个任务,只要您说它是完成的,就可以使用您想要的任何异步构造,这可能涉及到也可能不涉及使用其他线程。
发布于 2013-06-03 06:53:10
回答你的问题:
我能连接到异步/等待使用我自己的实现而不是任务吗?
是。你可以等待任何东西。不过,我不建议这样做。
如果这是不可能的,我是否可以在没有任何与“阻塞”和“线程”相关的情况下使用Task?
Task
类型代表未来。它不一定在线程上“运行”;它可以表示下载的完成,或者计时器过期等等。
有什么好的参考吗?当我写异步并等待时会发生什么?
如果您的意思是就代码转换而言,这篇博客文章有一个很好的并排。这并不是百分之百准确的细节,但它足以写一个简单的定制侍者。
如果你真的想扭曲async
做你的出价,乔恩斯基特的教育异步系列是最好的资源。不过,我真的不建议您在生产中这样做。
您可能会发现我的简介在介绍async
概念和推荐的使用方法时很有帮助。官方MSDN文档也非常好。
我确实编写了适用于您的情况的类;它们为async
/await
方法定义了一个单线程上下文。您可以使用AsyncContextThread
的Factory
属性将工作(或发送消息)排队到它。
https://stackoverflow.com/questions/16899402
复制相似问题