很明显,我在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
将始终在与调用方不同的线程上执行,并且通过显式禁用上下文同步,我会很好,但显然我错了。
答案 0 :(得分:2)
这并不能直接回答您“为什么会出现这种僵局”的问题,但这是需要考虑的问题,而且评论的时间太长了。
如果您的目标是提供同步和异步方法,以使使用代码的任何人都可以选择使用任一方法,那么您有两个选择:
不要
您已经发现,像这样包装异步代码是有风险的,风险应该是:
呼叫者已知。那些使用Execute
方法的人可能不会知道它包装了一个异步方法,并可能导致问题。 (尤其是如果它在库中,并且他们无法轻松访问源代码)
由呼叫者假定。建议始终使用await
async
方法。如果使用您的代码的人真的想同步等待它,那么这种风险就属于他们。但是,如果您自己提供一个同步包装器,它们会怪您造成任何死锁。
因此您只需删除您的Execute
方法,人们就可以使用它。
采用其他方式
如果您确实要提供同步版本,则可以遵循Microsoft设置的模式。它们有时确实提供了同步和异步方法来完成相同的事情,但是它们的实现完全不同。他们不只是包装异步方法。
您可以在他们的源代码中看到它。例如,将File.InternalReadAllText()
(由File.ReadAllText()
使用)与File.InternalReadAllTextAsync()
(由File.ReadAllTextAsync()
使用)进行比较。
因此,编写一个不使用任何异步方法的同步版本。