任务<t>撤销结果的扩展方法

时间:2016-10-18 23:34:07

标签: c# async-await task extension-methods

想知道,使用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标记的方法中。

1 个答案:

答案 0 :(得分:5)

你的代码将不会像你想要的那样工作,因为你正在传递一个&#34; Hot Task&#34;到功能。

我认为你这样做的原因是为了防止只是调用task.Result的死锁。发生死锁的原因是您阻止了UI线程,并且任务的捕获同步上下文使用UI线程进行回发。问题是当任务开始时没有等待它时捕获上下文。

所以,如果你在你的UI线程上做了

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

您仍然会陷入僵局,因为SynchronizationContext捕获的SomeMethodAsync()是UI上下文,await内部SomeMethodAsync()内部.ConfiguerAwait(false)不使用.Wait() }将尝试使用将在Await()Func<Task<T>>调用阻止的UI线程。

可靠地使其工作的唯一方法是,如果方法采用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; } ,则可以在后台线程中启动任务以确保同步上下文不是集。

   J
     o
       h
         n
   S
     m 
       i
         t
           h