Тупик при синхронном вызове асинхронного метода - PullRequest
0 голосов
/ 05 ноября 2019

Очевидно, что я создал здесь тупик в моем 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 Ответ

2 голосов
/ 05 ноября 2019

Это не дает прямого ответа на ваш вопрос «почему возникает эта тупиковая ситуация», но есть над чем подумать, и это слишком долго для комментариев.

Если ваша цель - предоставить синхронные и асинхронные методыпросто чтобы дать кому-либо, использующему ваш код, возможность использовать любой из них, у вас есть два варианта:

Не

Как вы обнаружили, оборачивая асинхронный код следующим образом:рискованно, и риск должен быть:

  1. Известен вызывающим абонентом. Те, кто использует метод Execute, вероятно, не будут знать, что он использует асинхронный метод и может вызвать проблемы. (особенно если это в библиотеке, и у них нет легкого доступа к исходному коду)

  2. Предполагается вызывающей стороной. Рекомендуется всегда использовать метод await async. Если кто-то, использующий ваш код, действительно хочет ждать его синхронно, тогда этот риск лежит на них. Но если вы предоставите синхронную оболочку самостоятельно, они будут обвинять вас в любых тупиках.

Так что вы можете просто удалить свой метод Execute, и люди могут с ним справиться.

Сделайте это по-другому

Если вы действительно хотите предоставить синхронную версию, вы можете следовать шаблону, установленному Microsoft. Иногда они предоставляют синхронные и асинхронные методы для выполнения одной и той же задачи, но их реализации полностью отличаются. Они не просто оборачивают асинхронный метод.

Вы можете увидеть это в их исходном коде. Например, сравните File.InternalReadAllText() (который используется File.ReadAllText()) с File.InternalReadAllTextAsync() (который используется File.ReadAllTextAsync()).

Итак, напишите синхронную версию, в которой не используются любые асинхронные методы.

...