10

我对C# 4.5 中的 new async/await关键字和类的可定制性有疑问。Task

首先了解我的问题的一些背景:我正在开发具有以下设计的框架:

  • 一个线程有一个“当前要做的事情”列表(通常大约 100 到 200 个项目),这些列表作为自己的数据结构存储并作为列表保存。它有一个Update()函数可以枚举列表并查看是否需要执行某些“事物”并执行。基本上它就像一个大线程sheduler。为了简化事情,让我们假设“要做的事情”是true在“完成”(并且不应该被称为下一次更新)以及false调度程序应该在下一次更新时再次调用它们时返回布尔值的函数。
  • 所有的“事物”都不能同时运行,也必须在这个线程中运行(因为线程静态变量)
  • 还有其他线程可以做其他事情。Update()它们的结构是相同的:大循环在一个大函数中迭代数百件要做的事情。
  • 线程可以相互发送消息,包括“远程过程调用”。对于这些远程调用,RPC 系统将某种未来对象返回到结果值。在另一个线程中,插入了一个新的“要做的事情”。
  • 一个常见的“事情”就是链接在一起的 RPC 序列。目前,这种“链接”的语法非常冗长和复杂,因为您必须手动检查先前 RPC 的完成状态并调用下一个等。

一个例子:

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;
}

现在这一切听起来都很棒asyncawaitC# 5.0 可以在这里帮助我。我还没有 100% 完全理解它在幕后做了什么(有什么好的参考资料吗?),但是当我从我看过的一些谈话中得到它时,它用这个非常简单的代码完全符合我的要求:

async Task SomeThingToDo() // returning task is completed when this is finished.
{
    await Remote1.CallF1();
    await Remote2.CallF2();
}

但是我找不到一种方法来编写我的Update()函数来使这样的事情发生。async并且await似乎想使用Task- 类又似乎需要真正的线程?

到目前为止我最接近的“解决方案”:

第一个线程(正在运行SomeThingToDo)只调用一次它们的函数并存储返回的任务并测试每个Update()任务是否完成。

Remote1.CallF1返回一个带有空操作作为构造函数参数的新任务,并记住返回的任务。当 F1 实际完成时,它会调用RunSynchronously()任务将其标记为已完成。

在我看来,这似乎是对任务系统的一种变态。此外,它会在两个线程之间创建共享内存(任务的IsComplete布尔值),如果可能的话,我想用我们的远程消息传递系统替换它。

最后,它不能解决我的问题,因为它不适用于SomeThingToDo上面的类似等待的实现。似乎异步函数返回的自动生成的任务对象立即完成?

所以最后我的问题:

  1. 我可以连接到 async/await 以使用我自己的实现而不是Task<T>?
  2. 如果那不可能,我可以Task在没有任何与“阻塞”和“线程”相关的情况下使用吗?
  3. async有什么好的参考,当我写和时到底发生了什么await
4

3 回答 3

13

我还没有 100% 完全理解它在幕后的作用——有什么好的参考吗?

早在我们设计 Mads 功能时,Stephen 和我就为 MSDN 杂志撰写了一些不同级别的文章。链接在这里:

http://blogs.msdn.com/b/ericlippert/archive/2011/10/03/async-articles.aspx

从我的文章开始,然后是 Mads 的,然后是 Stephen 的。

似乎异步函数返回的自动生成的任务对象立即完成?

不,它们在方法体中的代码返回或抛出时完成,与任何其他代码相同。

我可以连接到 async/await 以使用我自己的实现而不是Task<T>?

包含await必须返回的方法voidTaskTask<T>。但是,等待的表达式可以返回任何类型,只要您可以调用GetAwaiter()它。那不一定是Task.

如果那不可能,我可以在没有任何与“阻塞”和“线程”相关的情况下使用任务吗?

绝对地。A Taskjust 代表将在未来完成的工作。虽然这项工作通常在另一个线程上完成,但没有要求

于 2013-06-03T14:58:50.900 回答
5

要回答您的问题:

我可以挂接到 async/await 以使用我自己的实现而不是 Task 吗?

是的。你可以等待任何事情。但是,我不建议这样做。

如果那不可能,我可以在没有任何与“阻塞”和“线程”相关的情况下使用任务吗?

类型Task代表未来。它不一定在线程上“运行”;它可以代表下载完成,或者计时器到期等。

任何好的参考当我写异步和等待时到底发生了什么?

如果您的意思是就代码转换而言,这篇博客文章有一个很好的并排。它的细节不是 100% 准确,但编写一个简单的自定义等待程序就足够了。


如果你真的想扭转局面async,Jon Skeet 的eduasync系列是最好的资源。但是,我严重不建议您在生产中执行此操作。

您可能会发现我的async/ awaitintro作为async概念介绍和使用它们的推荐方法很有帮助。MSDN 官方文档也异常出色。

我确实编写了可能适用于您的情况的AsyncContextAsyncContextThread类;async他们为/await方法定义了一个单线程上下文。您可以AsyncContextThread使用其Factory属性将工作(或发送消息)排队。

于 2013-06-03T14:53:10.980 回答
5

我可以挂接到 async/await 以使用我自己的实现而不是 Task 吗?

是的。

如果那不可能,我可以在没有任何与“阻塞”和“线程”相关的情况下使用任务吗?

是的。

任何好的参考当我写异步和等待时到底发生了什么?

是的。

我会劝阻你不要问是/否问题。您可能不只是想要是/否的答案。

async 和 await 似乎想要使用 Task - 类又似乎需要真正的线程?

不,那不是真的。ATask代表可以在未来某个时间点完成的事情,可能会有结果。有时它是另一个线程中某些计算的结果,但不一定这样。它可以是未来某个时间点发生的任何事情。例如,它可能是 IO 操作的结果。

Remote1.CallF1返回一个带有空操作作为构造函数参数的新任务,并记住返回的任务。当 F1 实际完成时,它会调用RunSynchronously()任务将其标记为已完成。

所以你在这里缺少的是TaskCompletionSource课程。有了那个缺失的拼图,很多东西应该就位了。您可以创建 TCS 对象,将Task它的Task属性传递给...任何人,然后使用该SetResult属性表示它已完成。这样做不会导致创建任何额外的线程,也不会使用线程池。

请注意,如果您没有结果并且只想要 aTask而不是 aTask<T>那么只需使用 aTaskCompletionSource<bool>或沿这些线的东西然后SetResult(false)或任何合适的东西。通过将 转换Task<bool>为 a Task,您可以从公共 API 中隐藏该实现。

这也应该提供您提出的前两个问题的“如何”变体,而不是您提出的“我可以”版本。您可以使用 aTaskCompletionSource来生成一个任务,只要您说它完成,就可以使用您想要的任何异步构造,这可能涉及也可能不涉及使用额外的线程。

于 2013-06-03T14:52:56.937 回答