3

上个月我问了以下问题,这导致我学习了TaskEx.Yield

在第一个“等待”之前,异步方法可以有昂贵的代码吗?

但是,我后来意识到这个方法实际上将所有后续代码提交给环境TaskScheduler。本着真正的 DI 精神,我们的团队已同意尽可能避免使用环境实例,所以我想知道是否可以明确指定TaskScheduler要使用的 a?

像下面这样的东西会很棒:

public static YieldAwaitable Yield(TaskScheduler taskScheduler)
{
    return new YieldAwaitable(taskScheduler);
}

但是,当前的异步 CTP 实现仅提供:

public static YieldAwaitable Yield()
{
    return new YieldAwaitable(SynchronizationContext.Current ?? TaskScheduler.Current);
}

以下是否会提供可接受的有效替代方案?

await Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, this.TaskScheduler);
4

2 回答 2

5

本着真正的 DI 精神,我们的团队已同意尽可能避免使用环境实例……

异步语言支持基于隐式调度上下文。我认为这里不需要依赖注入。如有必要,任何调用您的方法的async方法都可以提供自己的上下文。

您的替代方案:

await Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, this.TaskScheduler);

不会按预期工作。这会将 noop lambda 排队到特定的TaskScheduler,然后在隐式调度上下文中恢复该方法。

早期版本的 Async CTP 确实提供了一种“屈服于另一个上下文”的方法,称为SwitchTo. 它被删除了,因为它太容易被滥用。

就个人而言,我认为async使用其调用方法提供的隐式调度上下文来保持代码更简洁。

PS 创建和安装您自己的上下文并不(太)困难,例如,用于测试目的。AsyncContext我为单元测试和控制台程序编写了一个简单的调度上下文。Async CTP 带有GeneralThreadAffineContextWindowsFormsContext和 ,WpfContext用于测试。其中任何一个都可以使用SynchronizationContext.SetSynchronizationContext. IMO,DI是矫枉过正。

于 2012-01-05T23:02:24.033 回答
2

对 async 的语言支持允许 awaitables 控制自己的调度。当您等待任务时,会启动默认调度,但是您可以通过多种方式使用更多代码来更改默认行为。

具体来说, await 旨在执行以下操作:

  1. 测试可等待对象是否“完成”(GetAwaiter().IsCompleted)。
  2. 如果(且仅当)未完成可等待对象,请要求它安排方法的其余部分(GetAwaiter().OnCompleted(...))
  3. “实现”等待的结果。这意味着要么返回返回值,要么确保遇到的异常重新出现。

所以请记住,如果 awaitable 声称它已“完成”,那么什么都不会安排。

Task.Yield() 是新颖的,因为它返回了一个从未完成的等待对象,其明确目的是为您提供一种方法来显式地暂时停止执行,并立即安排其余的执行。它使用环境上下文,但还有许多其他方法可以在没有环境上下文的情况下进行类似的操作。

覆盖默认行为的一个示例是当您等待未完成的任务,但使用ConfigureAwait(false)方法时。ConfigureAwait(false) 将任务包装在一个特殊的 awaitable 中,它始终使用默认任务调度程序,有效地始终在线程池上恢复。'false' 用于显式忽略环境同步上下文。

没有 Task.Yield().ConfigureAwait(false),但考虑以下假设:

// ... A ...
await Task.Yield().ConfigureAwait(false);
// ... B ...

以上可以通过以下方式实现

// ... A ...
await Task.Run(() => {
    // ... B ...
});

There is a little more explicitness and nesting there, but this isn't necessarily bad considering what's happening. Part 'A' always runs on the invoking thread, whereas part 'B' always runs on the thread pool. There are definitely differences on how you should look at code that is in sections A and B, so thus having some more curlies in between should hopefully get people to pause before assuming both sections are the same context.

于 2012-01-06T04:48:23.657 回答