Obviously I created a deadlock here in my Execute
Method which basically wraps an asynchronous implemtation.
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);
}
While ExecuteAsync
is working just fine Execute
does not terminate. I don't get the problem. It seems to be a deadlock but I don't see the cause.
Wrapping ExecuteAsync
like this works, though:
return Task.Run(
async () =>
await this.ExecuteAsync(batchId, parameterId, startDateTime, endDateTime, maxNumberOfDataPoints)
.ConfigureAwait(false))
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
Main difference is of course that ExecuteAsync
is being wrapped in a Task.
UPDATE: I thought that ExecuteAsync
will be always executed on a different thread than the callers and by explicitley disabling context sync I would be fine but obviously I am wrong.
This doesn't directly answer your question of "why does this deadlock", but it's something to think about and it was getting too long for comment.
If your goal is to provide synchronous and asynchronous methods just to give anyone using your code the option of using either, you have two options:
Don't
As you have found, wrapping asynchronous code like this is risky, and the risk should be:
Known by the caller. Those using the Execute
method probably won't know that it wraps an asynchronous method and could cause problems. (especially if this is in a library and they don't have easy access to the source code)
Assumed by the caller. The recommendation is always to await
an async
method. If someone using your code really wants to wait on it synchronously, then that risk belongs with them. But if you provide a synchronous wrapper yourself, they'll blame you for any deadlocks.
So you could just delete your Execute
method and people can deal with it.
Do it differently
If you really want to provide a synchronous version, you can follow the pattern set by Microsoft. They do sometimes provide synchronous and asynchronous methods to do the same thing, but their implementations are completely different. They don't just wrap the asynchronous method.
You can see this in their source code. For example, compare File.InternalReadAllText()
(which is used by File.ReadAllText()
) with File.InternalReadAllTextAsync()
(which is used by File.ReadAllTextAsync()
).
So, write a synchronous version that doesn't use any asynchronous methods.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.