我最近阅读了一些使用了大量异步方法的代码,但有时需要同步执行它们。代码如下:

Foo foo = GetFooAsync(...).GetAwaiter().GetResult();

是一样的吗

Foo foo = GetFooAsync(...).Result;

当前回答

如前所述,如果你可以使用await。如果你需要像你提到的. getawaiter (). getresult()那样同步运行代码,. result或. wait()有死锁的风险,正如许多人在评论/回答中所说的那样。因为我们大多数人都喜欢一行程序,所以你可以在。net 4.5中使用它们。

通过async方法获取一个值:

var result = Task.Run(() => asyncGetValue()).Result;

同步调用异步方法

Task.Run(() => asyncMethod()).Wait();

由于使用Task.Run,不会出现死锁问题。

来源:

https://stackoverflow.com/a/32429753/3850405

更新:

如果调用线程来自线程池,则可能导致死锁。发生如下情况:一个新任务排队到队列的末尾,最终将执行该任务的线程池线程被阻塞,直到该任务执行为止。

来源:

https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d

其他回答

我查看了TaskOfResult.cs的源代码(TaskOfResult.cs的源代码):

如果任务未完成,则Task。结果将调用getter中的Task.Wait()方法。

public TResult Result
{
    get
    {
        // If the result has not been calculated yet, wait for it.
        if (!IsCompleted)
        {
            // We call NOCTD for two reasons: 
            //    1. If the task runs on another thread, then we definitely need to notify that thread-slipping is required.
            //    2. If the task runs inline but takes some time to complete, it will suffer ThreadAbort with possible state corruption.
            //         - it is best to prevent this unless the user explicitly asks to view the value with thread-slipping enabled.
            //#if !PFX_LEGACY_3_5
            //                    Debugger.NotifyOfCrossThreadDependency();  
            //#endif
            Wait();
        }

        // Throw an exception if appropriate.
        ThrowIfExceptional(!m_resultWasSet);

        // We shouldn't be here if the result has not been set.
        Contract.Assert(m_resultWasSet, "Task<T>.Result getter: Expected result to have been set.");

        return m_result;
    }
    internal set
    {
        Contract.Assert(m_valueSelector == null, "Task<T>.Result_set: m_valueSelector != null");

        if (!TrySetResult(value))
        {
            throw new InvalidOperationException(Strings.TaskT_TransitionToFinal_AlreadyCompleted);
        }
    }
}

如果我们调用Task的GetAwaiter方法,Task将包装TaskAwaiter<TResult> (GetAwaiter的源代码()),(TaskAwaiter的源代码):

public TaskAwaiter GetAwaiter()
{
    return new TaskAwaiter(this);
}

如果我们调用TaskAwaiter<TResult>的GetResult()方法,它将调用Task。结果属性,即Task。结果将调用任务的Wait()方法(GetResult()的源代码):

public TResult GetResult()
{
    TaskAwaiter.ValidateEnd(m_task);
    return m_task.Result;
}

它是ValidateEnd(任务任务)的源代码(ValidateEnd(任务任务)的源代码):

internal static void ValidateEnd(Task task)
{
    if (task.Status != TaskStatus.RanToCompletion)
         HandleNonSuccess(task);
}

private static void HandleNonSuccess(Task task)
{
    if (!task.IsCompleted)
    {
        try { task.Wait(); }
        catch { }
    }
    if (task.Status != TaskStatus.RanToCompletion)
    {
        ThrowForNonSuccess(task);
    }
}

这是我的结论:

可以看到GetResult()正在调用TaskAwaiter.ValidateEnd(…),因此Task。结果不是相同的GetAwaiter.GetResult()。

我认为GetAwaiter(). getresult()是一个更好的选择,而不是. result,因为后者不包装异常。

我在《c# 7 in a Nutshell》(Joseph Albahari & Ben Albahari)的第582页读到了这一点。

如果一个先行任务出现错误,异常将在 延续代码调用awaiter.GetResult()。而不是打电话 的Result属性,我们可以简单地访问 先行词。调用GetResult的好处是如果 如果是前因错误,则直接抛出异常 封装在AggregateException中,允许更简单和更干净 catch块。

来源:c# 7 in a Nutshell的第582页

编辑:这是我13岁时写的,现在已经过时了。我推荐Nitin Agarwal的答案。

差不多。但有一个小区别:如果Task失败,GetResult()将直接抛出异常,而Task。结果将抛出AggregateException。然而,当它是异步的时候,使用它们有什么意义呢?更好的100倍选择是使用await。

另外,您不应该使用GetResult()。它仅供编译器使用,而不是供您使用。但是如果你不想要恼人的AggregateException,那就使用它。

另一个区别是,当async函数只返回Task而不是Task<T>时,您不能使用

GetFooAsync(...).Result;

GetFooAsync(...).GetAwaiter().GetResult();

仍能工作。

我知道问题中的示例代码是针对Task<T>的情况,但是这个问题是一般的。

如前所述,如果你可以使用await。如果你需要像你提到的. getawaiter (). getresult()那样同步运行代码,. result或. wait()有死锁的风险,正如许多人在评论/回答中所说的那样。因为我们大多数人都喜欢一行程序,所以你可以在。net 4.5中使用它们。

通过async方法获取一个值:

var result = Task.Run(() => asyncGetValue()).Result;

同步调用异步方法

Task.Run(() => asyncMethod()).Wait();

由于使用Task.Run,不会出现死锁问题。

来源:

https://stackoverflow.com/a/32429753/3850405

更新:

如果调用线程来自线程池,则可能导致死锁。发生如下情况:一个新任务排队到队列的末尾,最终将执行该任务的线程池线程被阻塞,直到该任务执行为止。

来源:

https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d

https://github.com/aspnet/Security/issues/59

最后一点:你应该避免使用Task。结果和任务。等 尽可能多的,因为它们总是将内部异常封装在 并将该消息替换为通用消息(one或 出现了更多错误),这使得调试更加困难。即使 同步版本不应该经常使用,应该强烈使用 请考虑使用Task.GetAwaiter(). getresult()。