更新这个问题的目的是得到一个简单的答案Task.Run()和死锁.我非常理解不混合异步和同步的理论推理,我将它们铭记于心.我不是在向别人学习新事物; 我尽力做到这一点.有时候所有人都需要技术答案......
我有一个Dispose()需要调用异步方法的方法.由于95%的代码都是异步的,因此重构不是最佳选择.拥有IAsyncDisposable框架支持的(以及其他功能)将是理想的,但我们还没有.所以在同一时间,我需要找到一种可靠的方法从同步方法调用异步方法而不会发生死锁.
我宁愿不使用,ConfigureAwait(false)因为这使得责任分散在我的整个代码中,以便被调用者以某种方式行事,以防调用者是同步的.我宁愿在同步方法中做一些事情,因为它是一个不正常的bugger.
在阅读了Stephen Cleary关于另一个Task.Run()总是在线程池中调度甚至异步方法的问题的评论后,它让我思考.
在ASP.NET中的.NET 4.5或任何其他同步上下文中,将任务调度到当前线程/同一线程,如果我有一个异步方法:
private async Task MyAsyncMethod()
{
...
}
Run Code Online (Sandbox Code Playgroud)
我想从同步方法中调用它,我可以使用Task.Run()它Wait()来避免死锁,因为它将异步方法排队到线程池吗?
private void MySynchronousMethodLikeDisposeForExample()
{
// MyAsyncMethod will get queued to the thread pool
// so it shouldn't deadlock with the Wait() ??
Task.Run((Func<Task>)MyAsyncMethod).Wait();
}
Run Code Online (Sandbox Code Playgroud) 阅读太久了.使用Task.ConfigureAwait(continueOnCapturedContext: false)可能会引入冗余线程切换.我正在寻找一致的解决方案.
长版.隐藏的主要设计目标ConfigureAwait(false)是在可能的情况下减少冗余的SynchronizationContext.Post延续回调await.这通常意味着更少的线程切换和更少的UI线程工作.但是,它并不总是如何运作.
例如,有一个实现SomeAsyncApiAPI的第三方库.请注意ConfigureAwait(false),由于某些原因,此库中的任何位置都不使用:
// some library, SomeClass class
public static async Task<int> SomeAsyncApi()
{
TaskExt.Log("X1");
// await Task.Delay(1000) without ConfigureAwait(false);
// WithCompletionLog only shows the actual Task.Delay completion thread
// and doesn't change the awaiter behavior
await Task.Delay(1000).WithCompletionLog(step: "X1.5");
TaskExt.Log("X2");
return 42;
}
// logging helpers
public static partial class TaskExt
{
public static void Log(string step)
{
Debug.WriteLine(new { step, thread = Environment.CurrentManagedThreadId }); …Run Code Online (Sandbox Code Playgroud) 众所周知,在通用库中,ConfigureAwait(false)应该在每次await调用时使用,以避免继续当前的SynchronizationContext.
作为使用整个代码库的替代方法ConfigureAwait(false),可以在公共表面方法中将SynchronizationContext设置为null一次,并在返回给用户之前将其恢复.换一种说法:
public async Task SomeSurfaceMethod()
{
var callerSyncCtx = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
try
{
// Do work
}
finally
{
SynchronizationContext.SetSynchronizationContext(callerSyncCtx);
}
}
Run Code Online (Sandbox Code Playgroud)
这也可以包含在内以using获得更好的可读性.
这种方法是否有缺点,它最终不会产生相同的效果吗?
主要优点显然是可读性 - 删除所有ConfigureAwait(false)呼叫.它还可以降低忘记ConfigureAwait(false)某个地方的可能性(尽管分析人员可以减轻这种情况,并且可以说开发人员也可以忘记更改SynchronizationContext).
一个有点奇特的优点是没有嵌入在所有方法中捕获SynchronizationContext的选择.换句话说,在一种情况下,我可能想要使用SynchronizationContext运行方法X,而在另一种情况下,我可能想要运行相同的方法而没有一个.何时ConfigureAwait(false)嵌入到不可能的地方.当然,这是一个非常罕见的要求,但我在处理Npgsql时遇到了这个问题(触发了这个问题).
这是一个WinForms代码:
async void Form1_Load(object sender, EventArgs e)
{
// on the UI thread
Debug.WriteLine(new { where = "before",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread });
var tcs = new TaskCompletionSource<bool>();
this.BeginInvoke(new MethodInvoker(() => tcs.SetResult(true)));
await tcs.Task.ContinueWith(t => {
// still on the UI thread
Debug.WriteLine(new { where = "ContinueWith",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread });
}, TaskContinuationOptions.ExecuteSynchronously).ConfigureAwait(false);
// on a pool thread
Debug.WriteLine(new { where = "after",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread });
}
Run Code Online (Sandbox Code Playgroud)
输出:
{ where = before, ManagedThreadId = 10, IsThreadPoolThread = False }
{ where = ContinueWith, … 我有一个公共async方法,它调用3个不同的API来获取一些数据,然后将响应发布到下一个API.现在,根据斯蒂芬·克利里的文章,在这里,以避免死锁:
1.在"库"异步方法中,尽可能使用ConfigureAwait(false).
2.不要阻止任务; 一直使用async.
我想知道私有异步方法是否也是如此?ConfigureAwait(false)我在调用private异步方法时是否需要使用?所以沿途就行了
public async Task<int> ProcessAsync(ServiceTaskArgument arg)
{
// do i need to use ConfigureAwait here while calling private async method?
var response1 = await GetAPI1().ConfigureAwait(false);
// do i need to use ConfigureAwait here while calling private async method?
var response2= await PostAPI2(response1).ConfigureAwait(false);
// do i need to use ConfigureAwait here while calling private async method?
await PostAPI3(response2).ConfigureAwait(false);
return 1;
}
private async Task<string> GetAPI1()
{
var httpResponse = …Run Code Online (Sandbox Code Playgroud)