Я пишу приложение WinForms, которое передает данные на устройство класса USB HID. В моем приложении используется отличная Generic HID library 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?
Может ли кто-нибудь прояснить поведение, которое я наблюдаю?
источник
void
на то,Task
что вы сказали.GetAwaiter().GetResult()
Task
представляет выполнение метода, поэтомуreturn
значения размещаютсяTask.Result
, а исключения размещаютсяTask.Exception
. Сvoid
, компилятор не имеет нигде места исключения, поэтому они просто ререйз на поток пула потоков.Самое главное , что нужно знать о
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-интерфейсами, которые прилагают большие усилия для их предоставления.Ответ на «ожидание» не дожидается завершения вызова имеет несколько более подробных объяснений этих ключевых слов.
Тем временем, руководство @Stephen Cleary по поводу
async void
удержаний. Другие приятные объяснения почему можно найти на http://www.tonicodes.net/blog/why-you-should-almost- Never-write-void-asynchronous-methods/ и https://jaylee.org/archive/ 2012/07/08 / c-sharp-async-tips-and-tricks-part-2-async-void.htmlисточник
await
«асинхронном ожидании» - то есть, оно блокирует метод (если необходимо), но не поток . Поэтому имеет смысл говорить обRequestToSendOutputReport
«ожидании»,RequestToGetInputReport
даже если это не ожидание с блокировкой .Лучшее решение для ожидания AsynMethod до завершения задачи -
var result = Task.Run(async() => await yourAsyncMethod()).Result;
источник
Вот обходной путь с использованием флага:
//outside your event or method, but inside your class private bool IsExecuted = false; private async Task MethodA() { //Do Stuff Here IsExecuted = true; } . . . //Inside your event or method { await MethodA(); while (!isExecuted) Thread.Sleep(200); // <------- await MethodB(); }
источник
просто поместите Wait (), чтобы дождаться завершения задачи
GetInputReportViaInterruptTransfer().Wait();
источник
В следующем фрагменте кода показано, как обеспечить выполнение ожидаемого метода перед возвратом к вызывающей стороне. ОДНАКО, я бы не сказал, что это хорошая практика. Пожалуйста, отредактируйте мой ответ с пояснениями, если вы считаете иначе.
public async Task AnAsyncMethodThatCompletes() { await SomeAsyncMethod(); DoSomeMoreStuff(); await Task.Factory.StartNew(() => { }); // <-- This line here, at the end } await AnAsyncMethodThatCompletes(); Console.WriteLine("AnAsyncMethodThatCompletes() completed.")
источник
await
+ theConsole.WriteLine
- превратить его в aTask
, что теряет контроль между ними. так что ваше «решение» в конечном итоге даст aTask<T>
, который не решает проблему. ДвигаешьсяTask.Wait
будет фактически прекратить обработку (с тупиковыми возможностями и т.д.). Другими словами, наawait
самом деле не ждёт, а просто объединяет две асинхронно исполняемые части в однуTask
(которую кто-то может посмотреть или подождать)На самом деле я нашел это более полезным для функций, возвращающих IAsyncAction.
var task = asyncFunction(); while (task.Status == AsyncStatus.Completed) ;
источник