繁体   English   中英

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

[英]Deadlock when callin async method synchronously

显然,我在我的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被包装在一个任务中。

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

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

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

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

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

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

所以你可以删除你的Execute方法,人们可以处理它。

做不同的事

如果你真的想提供同步版本,你可以按照微软设定的模式。 它们有时确实提供同步和异步方法来做同样的事情,但它们的实现完全不同。 他们不只是包装异步方法。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM