且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

在同步方法中使用 Task.Run() 以避免死锁等待异步方法?

更新时间:2022-04-28 21:54:14

您似乎了解问题所涉及的风险,所以我将跳过讲座.

It seems you understand the risks involved in your question so I'll skip the lecture.

要回答您的实际问题:是的,您可以使用 Task.Run 将该工作卸载到没有 SynchronizationContext 的 ThreadPool 线程 所以没有真正的死锁风险.

To answer your actual question: Yes, you can just use Task.Run to offload that work to a ThreadPool thread which doesn't have a SynchronizationContext and so there's no real risk for a deadlock.

但是,仅仅因为没有 SC 就使用另一个线程是一种黑客行为,并且可能会很昂贵,因为在 ThreadPool 上安排要完成的工作它的成本.

However, using another thread just because it has no SC is somewhat of a hack and could be an expensive one since scheduling that work to be done on the ThreadPool has its costs.

IMO 更好、更清晰的解决方案是暂时使用 SynchronizationContext.SetSynchronizationContext 移除 SC,然后再恢复.这可以很容易地封装到 IDisposable 中,以便您可以在 using 范围内使用它:

A better and clearer solution IMO would be to simply remove the SC for the time being using SynchronizationContext.SetSynchronizationContext and restoring it afterwards. This can easily be encapsulated into an IDisposable so you can use it in a using scope:

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);
    }
}

用法:

private void MySynchronousMethodLikeDisposeForExample()
{
    using (NoSynchronizationContextScope.Enter())
    {
        MyAsyncMethod().Wait();
    }
}