如何等待异步方法完成
本文关键字:异步方法 等待 何等待 | 更新日期: 2023-09-27 18:01:36
我正在编写一个将数据传输到USB HID类设备的WinForms应用程序。我的应用程序使用了优秀的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循环时,我需要从设备读取一些数据。然而,设备不能立即响应,所以我需要等待这个呼叫返回,然后再继续。因为它目前存在,requesttotinputreport()是这样声明的:
private async void RequestToGetInputReport()
{
// lots of code prior to this
int bytesRead = await GetInputReportViaInterruptTransfer();
}
为了它的价值,GetInputReportViaInterruptTransfer()的声明看起来像这样:
internal async Task<int> GetInputReportViaInterruptTransfer()
不幸的是,我不是很熟悉。net 4.5中新的async/await技术的工作原理。我做了一点阅读关于等待关键字,这给我的印象是调用getputreportviainterrupttransfer()内部的requesttingtinputreport()会等待(也许它做?),但它似乎不像调用requesttingputreport()本身正在等待,因为我似乎几乎立即重新进入while循环?
谁能解释一下我看到的行为?
关于async
和await
最重要的事情是await
不等待相关调用完成。await
所做的是,如果操作已经完成,则立即并同步地返回操作的结果,或者,如果还没有完成,则安排继续执行async
方法的其余部分,然后将控制返回给调用者。当异步操作完成后,将执行预定的完成。
问题标题中特定问题的答案是通过调用适当的Wait
方法来阻止async
方法的返回值(应该是Task
或Task<T>
类型):
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所提供的各种异步机制的低效使用,这些机制花费了大量的精力来提供它们。
答案在" wait"Doesn& #39;t wait for completion of call对这些关键字有几个更详细的解释。
同时,@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
避免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();
}
等待AsynMethod完成任务的最佳解决方案是
var result = Task.Run(async() => await yourAsyncMethod()).Result;
设置Wait()等待任务完成
GetInputReportViaInterruptTransfer().Wait();
以上答案都是正确的,你不应该同步等待一个任务…除非你必须这么做!有时候,你想在一个你无法控制的接口实现中调用一个async方法,并且没有办法做到"async"。
这里有一个小类,至少包含损坏:
public class RunSynchronous
{
public static void Do(Func<Task> func) => Task.Run(func).GetAwaiter().GetResult();
public static T Do<T>(Func<Task<T>> func) => Task.Run(func).GetAwaiter().GetResult();
public static void Do(Func<ValueTask> func) => Do(() => func().AsTask());
public static T Do<T>(Func<ValueTask<T>> func) => Do(() => func().AsTask());
}
这个类使用. getawaiter。GetResult模式来实际地将异步转换为同步。但是,如果您在WPF或绑定到特定线程的另一个SynchronizationContext中单独这样做,则会死锁。这段代码通过将异步操作转移到未与特定线程同步的线程池来避免死锁。只要你不发疯,阻塞所有的线程池线程,你应该没问题。
的用法如下
return RunSynchronous.Do(()=>AsyncOperation(a,b,c));
将在线程池上启动AsynchronousOperation(a,b,c)并等待它返回。只要你不显式地同步回原始线程,你应该没问题。
下面的代码片段显示了一种确保等待的方法在返回调用者之前完成的方法。然而,我不会说这是一个很好的练习。如果你不这样认为,请编辑我的答案并加以解释。
public async Task AnAsyncMethodThatCompletes()
{
await SomeAsyncMethod();
DoSomeMoreStuff();
await Task.Factory.StartNew(() => { }); // <-- This line here, at the end
}
await AnAsyncMethodThatCompletes();
Console.WriteLine("AnAsyncMethodThatCompletes() completed.")
实际上我发现这对返回IAsyncAction的函数更有帮助。
var task = asyncFunction();
while (task.Status == AsyncStatus.Completed) ;