<T> 返回结果的任务扩展方法



我想知道,使用Task<T>的扩展方法是否会有任何问题:

public static T Await<T>(this Task<T> task)
{
    var result = default(T);
    Task.Run(async () => result = await task).Wait();
    return result;
}

对于那些您想从Task获得结果但您处于未标记async的方法的实例,这似乎是一个体面的时间节省。

您的代码将不会像您想要的那样工作,因为您正在向函数传递"Hot Task"。

我假设你这样做的原因是为了防止调用task.Result的死锁。发生死锁的原因是你阻塞了UI线程,任务捕获的同步上下文使用UI线程进行回发。问题是上下文是在任务开始时捕获的,而不是在等待任务时捕获的。

如果你在UI线程上做了

Task<Foo> task = SomeMethodAsync();
Foo result = task.Await();

你仍然会死锁,因为SomeMethodAsync()捕获的SynchronizationContext是UI上下文,而SomeMethodAsync()中没有使用.ConfiguerAwait(false)的任何内部await将尝试使用UI线程,这将被Await()中的.Wait()调用阻塞。

唯一可靠地让它工作的方法是,如果该方法采用Func<Task<T>>而不是Task<T>,那么你可以在后台线程中启动任务,以确保同步上下文没有设置。

public static T BlockWithoutLockup<T>(Func<Task<T>> task)
{
    T result;
    if(SynchronizationContext.Current != null)
    {
        //We use ".GetAwaiter().GetResult()" instead of .Result to get the exception handling 
        // like we would if we had called `await` or the function directly.
        result = Task.Run(task).GetAwaiter().GetResult();
    }
    else
    {
        //If we are on the default sync context already just run the code, no need to
        // spin up another thread.
        result = task().GetAwaiter().GetResult();
    }
    return result;
}

最新更新