Mik*_*sen 40 .net c# asynchronous task-parallel-library async-await
更新这个问题的目的是得到一个简单的答案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)
i3a*_*non 58
您似乎了解了问题中涉及的风险,因此我将跳过讲座.
回答你的实际问题:是的,你可以Task.Run
用来将这项工作卸载到一个ThreadPool
没有的线程中SynchronizationContext
,因此没有真正的死锁风险.
但是,仅仅因为它没有SC而使用另一个线程有点像黑客并且可能是一个昂贵的,因为调度工作要做的就是ThreadPool
有其成本.
一个更好,更清晰的解决方案IMO将暂时删除SC SynchronizationContext.SetSynchronizationContext
并在之后恢复它.这可以很容易地封装成一个,IDisposable
所以你可以在一个using
范围内使用它:
public static class NoSynchronizationContextScope
{
public static Disposable Enter()
{
var context = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
return new Disposable(context);
}
public struct Disposable : IDisposable
{
private readonly SynchronizationContext _synchronizationContext;
public Disposable(SynchronizationContext synchronizationContext)
{
_synchronizationContext = synchronizationContext;
}
public void Dispose() =>
SynchronizationContext.SetSynchronizationContext(_synchronizationContext);
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
private void MySynchronousMethodLikeDisposeForExample()
{
using (NoSynchronizationContextScope.Enter())
{
MyAsyncMethod().Wait();
}
}
Run Code Online (Sandbox Code Playgroud)
当我必须同步调用异步方法并且线程可以是 UI 线程时,这是我避免死锁的方法:
public static T GetResultSafe<T>(this Task<T> task)
{
if (SynchronizationContext.Current == null)
return task.Result;
if (task.IsCompleted)
return task.Result;
var tcs = new TaskCompletionSource<T>();
task.ContinueWith(t =>
{
var ex = t.Exception;
if (ex != null)
tcs.SetException(ex);
else
tcs.SetResult(t.Result);
}, TaskScheduler.Default);
return tcs.Task.Result;
}
Run Code Online (Sandbox Code Playgroud)