【问题标题】:Does implementation change the behaviour of await?实现会改变等待的行为吗?
【发布时间】:2015-11-17 18:51:24
【问题描述】:

Await 关键字与可等待类型一起使用(.NET 带有现有的两种此类类型,TaskTask<T>)。但是,您可以编写自己的可等待类型。

msdn blog post 声明:

你可以想到下面的代码:

await FooAsync();
RestOfMethod();

本质上与此相似:

var t = FooAsync();
var currentContext = SynchronizationContext.Current;
t.ContinueWith(delegate
{
    if (currentContext == null)
        RestOfMethod();
    else
        currentContext.Post(delegate { RestOfMethod(); }, null);
}, TaskScheduler.Current);

上面的(伪)代码是否遵循可等待类型的实现(例如Task),或者可能只是编译器处理任何可等待类型的方式await 关键字的右侧?

在下面的 cmets 部分,有一篇文章解释了 TaskScheduler 和 SynchronizationContext 之间的区别。

主要区别在于 SynchronizationContext 是一个通用的 与委托合作的机制,而 TaskScheduler 是 特定于并迎合任务(您可以获得一个 TaskScheduler 包装一个 SynchronizationContext 使用 TaskScheduler.FromCurrentSynchronizationContext)。这就是为什么 等待任务将两者都考虑在内,首先检查 SynchronizationContext(作为大多数 UI 中更通用的机制) 框架支持),然后回退到 TaskScheduler。 等待不同类型的对象可能会选择首先使用 SynchronizationContext,然后回退到其他一些机制 特定于该特定类型。

如果我正确理解最后一句话,这意味着我可以将我想要的任何委托放入continueWith 方法中(我的意思是上面代码示例中的t.ContinueWith 调用),即修改await 在与我的自定义等待对象。

以防万一您想了解更多: http://blogs.msdn.com/b/pfxteam/archive/2009/09/22/9898090.aspx

【问题讨论】:

标签: c# asynchronous async-await


【解决方案1】:

上面的(伪)代码是从 awaitable 类型(如 Task)的实现中继承而来,还是只是编译器处理 await 关键字右侧的任何 awaitable 类型的方式?

代码确实来自具体的任务实现。但它的存在只是为了帮助您了解它的工作原理,因为它与延续相似

编译器生成的实际代码差别很大,它没有考虑实际的等待类型。它所做的只是寻找一个GetAwaiter 方法,该方法返回一个具有IsCompletedGetResultOnCompleted 的等待者。可等待的实现是捕获或不捕获SynchronizationContext.Current(或完全执行其他操作)的实现。

你可以看到实际代码here

即完全修改 await 与我的自定义可等待对象一起使用时的工作方式。

只要对你有意义,你可以做任何你想做的事。您甚至可以使用扩展方法使内置类型可等待。例如这段代码:

public static Awaiter GetAwaiter(this string s)
{
    throw new NotImplementedException();
}
public abstract class Awaiter : INotifyCompletion
{
    public abstract bool IsCompleted { get; }
    public abstract void GetResult();
    public abstract void OnCompleted(Action continuation);
}

将使您能够编译await "bar";。它当然会在运行时失败,但编译器不知道。

【讨论】:

  • 那么我不明白我引用的最后一句话是什么意思:“等待不同类型的对象 [不同于任务,对吗?] 可能会选择首先使用 SynchronizationContext,然后下降回到特定于该特定类型的其他一些机制。”
  • @user4205580 表示等待类型选择做什么。编译器只是调用它。例如,当您调用 task.ConfigureAwait(false) 时,您会返回一个名为 ConfiguredTaskAwaitable 的不同等待对象,它根据传递给方法的布尔值选择是否使用 SynchronizationContext
  • @user4205580 所以await task 将发布到SynchronizationContext,如果有,但await task.ConfigureAwait(false) 不会。
  • @user4205580 这意味着等待Task捕获SynchronizationContext.Current的值是Task的awaiter实现的行为,不同的awaiter可以有不同的行为。捕获当前同步上下文不是await 的语言功能。
  • 在这种情况下,t.ContinueWith(delegate { if (currentContext == null) RestOfMethod(); else currentContext.Post(delegate { RestOfMethod(); }, null); 将不适用,因为它不会尝试在当前上下文中运行延续(我说的是您的情况,其中 tConfiguredTaskAwaitable)。所以引用的代码确实随着 awaitable 的实现而改变。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多