it-swarm.com.ru

Как ждать завершения асинхронного метода?

Я пишу приложение WinForms, которое передает данные на устройство класса USB HID. Мое приложение использует отличную библиотеку Generic HID v6.0, которую можно найти здесь . Короче говоря, когда мне нужно записать данные на устройство, это код, который вызывается:

private async void RequestToSendOutputReport(List<byte[]> byteArrays)
{
    foreach (byte[] b in byteArrays)
    {
        while (condition)
        {
            // we'll typically execute this code many times until the condition is no longer met
            Task t = SendOutputReportViaInterruptTransfer();
            await t;
        }

        // read some data from device; we need to wait for this to return
        RequestToGetInputReport();
    }
}

Когда мой код выпадает из цикла while, мне нужно прочитать некоторые данные с устройства. Однако устройство не может сразу ответить, поэтому мне нужно дождаться ответа на этот вызов, прежде чем я продолжу. Поскольку он существует в настоящее время, RequestToGetInputReport () объявлен так:

private async void RequestToGetInputReport()
{
    // lots of code prior to this
    int bytesRead = await GetInputReportViaInterruptTransfer();
}

Для чего это стоит, объявление для GetInputReportViaInterruptTransfer () выглядит так:

internal async Task<int> GetInputReportViaInterruptTransfer()

К сожалению, я не очень знаком с работой новых технологий async/await в .NET 4.5. Ранее я немного прочитал о ключевом слове await, и у меня сложилось впечатление, что вызов GetInputReportViaInterruptTransfer () внутри RequestToGetInputReport () будет ждать (и, возможно, так и будет?), Но это не похоже на вызов RequestToGetInputReport () сам ждет, потому что я, кажется, снова вхожу в цикл while почти сразу?

Кто-нибудь может прояснить поведение, которое я вижу?

117
bmt22033

Избегайте async void. Ваши методы должны возвращать Task вместо void. Тогда вы можете await их.

Как это:

private async Task RequestToSendOutputReport(List<byte[]> byteArrays)
{
    foreach (byte[] b in byteArrays)
    {
        while (condition)
        {
            // we'll typically execute this code many times until the condition is no longer met
            Task t = SendOutputReportViaInterruptTransfer();
            await t;
        }

        // read some data from device; we need to wait for this to return
        await RequestToGetInputReport();
    }
}

private async Task RequestToGetInputReport()
{
    // lots of code prior to this
    int bytesRead = await GetInputReportViaInterruptTransfer();
}
111
Stephen Cleary

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

Ответ на конкретный вопрос в заголовке вашего вопроса состоит в том, чтобы заблокировать возвращаемое значение метода async (которое должно иметь тип Task или Task<T>), вызвав соответствующий метод Wait:

public static async Task<Foo> GetFooAsync()
{
    // Start asynchronous operation(s) and return associated task.
    ...
}

public static Foo CallGetFooAsyncAndWaitOnResult()
{
    var task = GetFooAsync();
    task.Wait(); // Blocks current thread until GetFooAsync task completes
                 // For pedagogical use only: in general, don't do this!
    var result = task.Result;
    return result;
}

В этом фрагменте кода CallGetFooAsyncAndWaitOnResult является синхронной оберткой вокруг асинхронного метода GetFooAsync. Однако этого шаблона следует избегать по большей части, поскольку он будет блокировать весь поток пула потоков на время асинхронной операции. Это неэффективное использование различных асинхронных механизмов, предоставляемых API, которые прилагают большие усилия для их обеспечения.

Ответ на "await" не ожидает завершения вызова содержит несколько более подробных объяснений этих ключевых слов.

Между тем, руководство @Stephen Cleary о async void держится. Другие приятные объяснения причин можно найти по адресу http://www.tonicodes.net/blog/why-you-should-almost-never-write-void-asynchronous-methods/ и http://www.jaylee.org/post/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.aspx .

193
Richard Cook

Лучшее решение для ожидания AsynMethod до завершения задачи

var result = Task.Run(async() => { return await yourAsyncMethod(); }).Result;
55
Ram chittala