同步调用异步方法时出现死锁

时间:2019-11-05 17:08:23

标签: c# async-await task

很明显,我在Execute方法中在此处创建了一个死锁,该方法基本上可以包装 异步实现。

 public IEnumerable<IDataPoint> Execute(Guid batchId, Guid parameterId, DateTime startDateTime, DateTime endDateTime, int maxNumberOfDataPoints)
        {
            return this.ExecuteAsync(batchId, parameterId, startDateTime, endDateTime, maxNumberOfDataPoints)
                .ConfigureAwait(false)
                .GetAwaiter()
                .GetResult();
        }


public async Task<IEnumerable<IDataPoint>> ExecuteAsync(Guid batchId, Guid parameterId, DateTime startDateTime, DateTime endDateTime, int maxNumberOfDataPoints)
{
    var foundDataPoints = new List<DataPoint>();

    startDateTime = startDateTime.WithoutMilliseconds();
    var firstDataPoint = await this.GetFirstDataPointBeforeDateTimeAsync(batchId, parameterId, startDateTime).ConfigureAwait(false);
    var lastDataPoint = await this.GetFirstDataPointAfterDateTimeAsync(batchId, parameterId, endDateTime).ConfigureAwait(false);
    var numberOfDatapointsToSubstract = firstDataPoint == null ? 0 : 1;
    numberOfDatapointsToSubstract += lastDataPoint == null ? 0 : 1;
    var dataPoints = await this.GetDataPointsBetweenDateTimesAsync(batchId, parameterId, startDateTime, endDateTime, maxNumberOfDataPoints - numberOfDatapointsToSubstract).ConfigureAwait(false);

    if (firstDataPoint != null)
    {
        foundDataPoints.Add(firstDataPoint);
    }

    foundDataPoints.AddRange(dataPoints);

    if (lastDataPoint != null)
    {
        foundDataPoints.Add(lastDataPoint);
    }

    return foundDataPoints.OrderBy(x => x.Timestamp);
}

ExecuteAsync工作正常时,Execute不会终止。 我没问题。这似乎是一个僵局,但我看不出原因。

包裹ExecuteAsync就像这样工作,

return Task.Run(
                    async () =>
                        await this.ExecuteAsync(batchId, parameterId, startDateTime, endDateTime, maxNumberOfDataPoints)
                            .ConfigureAwait(false))
                .ConfigureAwait(false)
                .GetAwaiter()
                .GetResult();

主要区别当然是ExecuteAsync被包装在一个Task中。

更新: 我认为ExecuteAsync将始终在与调用方不同的线程上执行,并且通过显式禁用上下文同步,我会很好,但显然我错了。

1 个答案:

答案 0 :(得分:2)

这并不能直接回答您“为什么会出现这种僵局”的问题,但这是需要考虑的问题,而且评论的时间太长了。

如果您的目标是提供同步和异步方法,以使使用代码的任何人都可以选择使用任一方法,那么您有两个选择:

不要

您已经发现,像这样包装异步代码是有风险的,风险应该是:

  1. 呼叫者已知。那些使用Execute方法的人可能不会知道它包装了一个异步方法,并可能导致问题。 (尤其是如果它在库中,并且他们无法轻松访问源代码)

  2. 由呼叫者假定。建议始终使用await async方法。如果使用您的代码的人真的想同步等待它,那么这种风险就属于他们。但是,如果您自己提供一个同步包装器,它们会怪您造成任何死锁。

因此您只需删除您的Execute方法,人们就可以使用它。

采用其他方式

如果您确实要提供同步版本,则可以遵循Microsoft设置的模式。它们有时确实提供了同步和异步方法来完成相同的事情,但是它们的实现完全不同。他们不只是包装异步方法。

您可以在他们的源代码中看到它。例如,将File.InternalReadAllText()(由File.ReadAllText()使用)与File.InternalReadAllTextAsync()(由File.ReadAllTextAsync()使用)进行比较。

因此,编写一个不使用任何异步方法的同步版本。