2013-03-01 3 views
77

Я пишу приложение WinForms, которое передает данные на USB-устройство класса HID. В моем приложении используется отличная универсальная HID-библиотека v6.0, которая может быть найдена here. В двух словах, когда мне нужно записать данные на устройство, это код, который вызывается:Как дождаться завершения процедуры async?

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(); 
    } 
} 

Когда мой код выпадает из цикла в то время, мне нужно прочитать некоторые данные из устройства. Однако устройство не может ответить сразу, поэтому мне нужно дождаться, пока этот вызов вернется, прежде чем продолжить. В настоящее время он существует, 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. Я немного читал ранее о ключевом слове «Ожидание», и это создавало впечатление, что вызов GetInputReportViaInterruptTransfer() внутри RequestToGetInputReport() будет ждать (и, возможно, это так?), Но это не похоже на вызов RequestToGetInputReport() сам ждет, потому что я, кажется, сразу возвращаюсь в цикл while почти сразу?

Может ли кто-нибудь уточнить поведение, которое я вижу?

ответ

88

Избегать 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(); 
} 
+1

Спасибо, Стивен. – user685869

+0

Очень приятно, спасибо. Я почесывал голову по аналогичной проблеме, и разница заключалась в том, чтобы изменить 'void' на' Task' так же, как вы сказали. – Jeremy

+5

Это второстепенная вещь, но для соблюдения соглашения оба метода должны иметь Async, добавленный к их именам, например. RequestToGetInputReportAsync() – mayu

128

Самое главное, что нужно знать о 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; 
} 

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

Ответ на вопрос "await" doesn't wait for the completion of call содержит несколько подробных объяснений этих ключевых слов.

Между тем, руководство @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.

+14

Мне полезно думать (и говорить) о 'await' как о« асинхронном ожидании », т. Е. Блокирует * метод * (при необходимости) но не * thread *. Поэтому имеет смысл говорить о 'RequestToSendOutputReport', ожидающем« RequestToGetInputReport », даже если это не * блокировка * ожидания. –

+0

@ Рихард Кук - большое спасибо за дополнительное объяснение! – user685869

+5

Это должен быть принятый ответ, так как он более четко отвечает на фактический вопрос (например, как потоковый блок по методу async). – csvan

-6

В следующем фрагменте показан способ обеспечения ожидаемого метода до его возврата к вызывающему абоненту. ОДНАКО, я бы не сказал, что это хорошая практика. Пожалуйста, отредактируйте мой ответ с объяснениями, если вы думаете иначе.

public async Task AnAsyncMethodThatCompletes() 
{ 
    await SomeAsyncMethod(); 
    DoSomeMoreStuff(); 
    await Task.Factory.StartNew(() => { }); // <-- This line here, at the end 
} 

await AnAsyncMethodThatCompletes(); 
Console.WriteLine("AnAsyncMethodThatCompletes() completed.") 
+0

Downvoters, помогите объяснить, как я спросил в ответе? Потому что это хорошо работает, насколько я знаю ... – Jerther

+2

Проблема в том, что единственным способом, которым вы можете выполнить 'await' +' Console.WriteLine', является то, что он становится «Задачей», которая отказывается от контроля между двумя , поэтому ваше «решение» в конечном итоге даст «Задача », которая не решает проблему. Выполнение ['Task.Wait' будет] (http://stackoverflow.com/questions/32075084/manage-update-in-application-exit/32075919#) фактически прекратит обработку (с возможностями взаимоблокировки и т. Д.). Другими словами, 'await' на самом деле не ждет, он просто объединяет две асинхронно исполняемые части в одну« Задачу »(что кто-то может наблюдать или ждать) –

18

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

var result = Task.Run(async() => { return await yourAsyncMethod(); }).Result; 
+1

Или это для вашего асинхронного« void »: \t Task.Run (async() => {ожидание yourAsyncMethod();}). Wait(); –

Смежные вопросы