5

考虑以下代码:

public Task SomeAsyncMethod()
{
    var tcs = new TaskCompletionSource();
    ... do something, NOT setting the TaskCompletionSource...

    return tcs.Task
}

public void Callsite1()
{
    await SomeAsyncMethod();
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
}

public void Callsite2()
{
    SomeAsyncMethod().ContinueWith((task) =>
    {
        Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
    });
}

在某个时间点,在 SomeAsyncMethod 中创建的 TaskCompletionSource 设置在 ThreadPool 线程上:

Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
tcs.SetResult();

当等待来自 TaskCompletionSource 的任务时(如在 Callsite1 中),继续在调用 SetResult 的线程上同步执行。当调用 ContinueWith 时(如在 Callsite2 中),延续在不同的线程上异步执行。

调用配置等待无济于事,如

public void Callsite1()
{
    await SomeAsyncMethod().ConfigureAwait(true or false);
}

这甚至不是这个问题的重点。作为 SomeAsyncMethod 的实现者,我不想通过调用 SetResult 来调用一些可能未知的代码。我想让延续总是异步安排。而且我不能依靠调用者来正确配置等待(如果这甚至可以工作的话)。

如何配置 TaskCompletionSource 以使其 Task 在等待时不同步执行其继续?

4

3 回答 3

12

没有办法阻止同步任务继续。通常,这不是问题。

但是,在某些情况下您确实需要这样做,例如,如果您在持有锁的同时完成任务。在这些情况下,您可以只Task.Run完成任务,如下所示:

// Set the result on a threadpool thread, so any synchronous continuations
//  will execute in the background.
Task.Run(() => tcs.TrySetResult(result));

// Wait for the TCS task to complete; note that the continuations
//  may not be complete.
tcs.Task.Wait();

这是一项先进的技术。这是我的博客中阐述的“不要阻塞async代码(async一直向下)”指南的一个例外。

它是我的AsyncEx library的一部分,作为扩展方法:

public static void TrySetResultWithBackgroundContinuations<TResult>(
    this TaskCompletionSource<TResult> @this, TResult result);

这项技术最早由 Stephen Toub 在他的博客上发表

于 2012-10-02T17:00:45.600 回答
2

如何配置 TaskCompletionSource 以使其 Task 在等待时不同步执行其继续?

不可能。您正在公开Task公开,一旦您这样做,任何人都可以自由附加同步延续(他们只需要使用另一个重载ContinueWith,他们不需要使用 async/await)。

于 2012-10-02T15:39:05.827 回答
2

从 .NET 4.6 开始,TaskCreationOption.RunContinuationsAsynchronously仅针对这种情况添加:

[它强制]添加到当前任务的延续被异步执行。

因此,您可以创建一个TaskCompletionSourcewithTaskCreationOptions.RunContinuationsAsynchronously并确保调用它.SetResult()不会同步执行随机代码。

var tcs = new TaskCompletionSource<bool>(
    TaskCreationOptions.RunContinuationsAsynchronously);

// some time later, somewhere else
tcs.SetResult(true);
DoMoreStuffWithoutWorryingThatSetResultJustRanRandomCode();

Stephen Toub 的博文“.NET 4.6 中的新任务 API”中提供了更多详细信息。

当使用此选项创建的任务完成时,它甚至不会尝试同步调用延续......它只会异步调用所有延续,就好像没有人要求尽可能同步执行一样。

如果TaskContinuationOptions.RunContinuationsAsynchronously您需要类似的行为,例如.ContinueWith().

[TaskContinuationOptions.RunContinuationsAsynchronously指定]应异步运行延续任务。此选项优先于 TaskContinuationOptions.ExecuteSynchronously。

于 2016-05-27T20:52:48.690 回答