请考虑以下代码:
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的Task(如在Callsite1中)时,继续在调用SetResult的Thread上同步执行。调用ContinueWith时(如在Callsite2中),继续在另一个线程上异步执行。
在
中调用configure await没有帮助public void Callsite1()
{
await SomeAsyncMethod().ConfigureAwait(true or false);
}
这甚至不是这个问题的重点。作为SomeAsyncMethod的实现者,我不想通过调用SetResult来调用一些可能未知的代码。我希望延迟始终是异步安排的。并且我不能依赖调用者来正确配置await(如果这甚至可以工作)。
如何配置TaskCompletionSource,使其任务在等待时不会同步执行其继续?
答案 0 :(得分:11)
无法阻止同步任务延续。通常情况下,这不是问题。
但是,在某些情况下您需要这样做,例如,如果您在持有锁定的同时完成任务。在这些情况下,您可以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();
这是一项先进的技术。这是guideline "Don't block on async
code (async
all the way down)" as expounded on my blog的例外。
这是我AsyncEx library的一部分,作为扩展方法:
public static void TrySetResultWithBackgroundContinuations<TResult>(
this TaskCompletionSource<TResult> @this, TResult result);
答案 1 :(得分:2)
如何配置TaskCompletionSource,使其任务在等待时不会同步执行其继续?
不可能。您公开展示Task
,一旦您这样做,任何人都可以自由附加同步延续(他们只需要使用ContinueWith
的另一个重载,他们不需要 使用async / await)。
答案 2 :(得分:1)
自.NET 4.6起,TaskCreationOption.RunContinuationsAsynchronously
仅针对此类或案例添加:
[强制]延续添加到当前任务中以异步方式执行。
因此,您可以使用TaskCompletionSource
创建TaskCreationOptions.RunContinuationsAsynchronously
,并确保调用其.SetResult()
不会同步执行随机代码。
var tcs = new TaskCompletionSource<bool>(
TaskCreationOptions.RunContinuationsAsynchronously);
// some time later, somewhere else
tcs.SetResult(true);
DoMoreStuffWithoutWorryingThatSetResultJustRanRandomCode();
Stephen Toub's blog post "New Task APIs in .NET 4.6"中提供了更多详细信息。
当使用此选项创建的任务完成时,它甚至不会尝试同步调用continuation ...它只是异步调用所有的continuation,就像没有人要求同步执行一样,如果可能的话
如果您需要类似.ContinueWith()
之类的行为,还有TaskContinuationOptions.RunContinuationsAsynchronously
。
[
TaskContinuationOptions.RunContinuationsAsynchronously
指定]延迟任务应该异步运行。此选项优先于TaskContinuationOptions.ExecuteSynchronously。