Task< T>返回结果的扩展方法
本文关键字:扩展 方法 结果 返回 Task | 更新日期: 2023-09-27 18:13:33
我想知道,使用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;
}