简体   繁体   English

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

[英]Deadlock when callin async method synchronously

Obviously I created a deadlock here in my Execute Method which basically wraps an asynchronous implemtation.显然,我在我的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);
}

While ExecuteAsync is working just fine Execute does not terminate.虽然ExecuteAsync工作得很好,但Execute不会终止。 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:但是,像这样包装ExecuteAsync是可行的:

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.主要区别当然是ExecuteAsync被包装在一个任务中。

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

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:正如您所发现的,像这样包装异步代码是有风险的,风险应该是:

  1. Known by the caller.被调用者知道。 Those using the Execute method probably won't know that it wraps an asynchronous method and could cause problems.那些使用Execute方法的人可能不知道它包装了一个异步方法并且可能会导致问题。 (especially if this is in a library and they don't have easy access to the source code) (特别是如果这是在一个库中并且他们无法轻松访问源代码)

  2. Assumed by the caller.由调用者假定。 The recommendation is always to await an async method.建议始终await async方法。 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.所以你可以删除你的Execute方法,人们可以处理它。

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() ).例如,将File.InternalReadAllText() (由File.ReadAllText()使用)与File.InternalReadAllTextAsync() (由File.ReadAllTextAsync()使用)进行比较。

So, write a synchronous version that doesn't use any asynchronous methods.因此,编写一个不使用任何异步方法的同步版本。

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

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