Как дождаться завершения асинхронного метода?
Я пишу приложение WinForms, которое передает данные на устройство класса USB HID. Мое приложение использует отличную универсальную библиотеку 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 почти сразу?
может ли кто-нибудь прояснить поведение, которое я вижу?
4 ответов:
избежать
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(); }
самое главное, чтобы знать о
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.
лучшим решением для ожидания AsynMethod до завершения задачи является
var result = Task.Run(async() => { return await yourAsyncMethod(); }).Result;
в следующем фрагменте кода показан способ обеспечения завершения ожидаемого метода перед возвращением к вызывающему объекту. Однако я бы не сказал, что это хорошая практика. Пожалуйста, отредактируйте мой ответ с объяснениями, если вы думаете иначе.
public async Task AnAsyncMethodThatCompletes() { await SomeAsyncMethod(); DoSomeMoreStuff(); await Task.Factory.StartNew(() => { }); // <-- This line here, at the end } await AnAsyncMethodThatCompletes(); Console.WriteLine("AnAsyncMethodThatCompletes() completed.")
Comments