我一直试图理解as#/ await和C#中的任务,但是尽管观看了YouTube视频,阅读文档和遵循复数课程,但仍然失败了 .
我希望有人可以帮助回答这些略微抽象的问题,以帮助我的大脑 .
1.为什么他们说async / await在它自己的async关键字什么都不做并且await关键字添加一个暂停点时启用'asynchonrous'方法?不是添加一个暂停点,强制该方法同步操作,即在继续之前完成await标记的任务 .
2.显然除了事件处理程序之外你不应该使用async void,那么你如何正常调用异步方法呢?似乎为了使用await关键字调用异步方法,调用它本身的方法/类需要标记为async . 我见过的所有示例都“启动”了一个带有事件处理程序的异步void方法 . 你如何“逃避”async / await的包装以运行该方法?
3 .
public async Task SaveScreenshot(string filename, IWebDriver driver)
{
var screenshot = driver.TakeScreenshot();
await Task.Run(() =>
{
Thread.Sleep(2000);
screenshot.SaveAsFile(filename, ScreenshotImageFormat.Bmp);
Console.WriteLine("Screenshot saved");
});
Console.WriteLine("End of method");
}
回到1.这看起来像一个同步方法 . 当执行到 Task.Run
时执行暂停,因此在任务完成之前不会执行 Console.WriteLine("End of method");
. 也许整个方法本身将在代码中触发它时异步执行?但是回到2,你需要等待调用它,否则你得到消息'Because this call is not awaited..'因此添加await会导致执行点同步等等 .
任何帮助理解这一点将非常感激 .
3 回答
不,你想到的这个词是"sequential",而不是"synchronous" .
await
导致异步顺序代码 . "Sequential"的意思"one at a time"; "synchronous"含义"blocking until completed" .使用
await
.理想情况下,你 don't . 你去async all the way . 现代框架(包括ASP.NET MVC,Azure Functions / WebJobs,NUnit / xUnit / MSTest等)都允许您拥有返回
Task
的入口点 . 不太现代的框架(包括WinForms,WPF,Xamarin Forms,ASP.NET WebForms等)都允许async void
入口点 .因此,理想情况下,您不会从同步代码中调用异步代码 . 如果你考虑异步代码 is 这是有意义的:它的全部意义是不阻塞调用线程,所以如果你阻塞异步代码上的调用线程,那么你首先会失去异步代码的所有好处 .
也就是说,在极少数情况下您需要同步处理代码 . 例如,如果您正处于异步转换的过程中,或者您受到强制您的代码同步且不能与
async void
一起使用的库/框架的约束 . 在这种情况下,您可以使用hacks in my article on brownfield async之一 .你的理解非常好:) . 您似乎缺少的主要观点是.NET中的“异步”方法意味着可以在不阻塞调用线程的情况下停止执行的方法 .
正如您在(1)中指出的那样,
async
关键字基本上允许使用await
,并且要求返回类型为void
或Task/Task<T>
.await
只是指示当前方法暂停执行,直到任务完成 .你在这里缺少的是它暂停 just the current method . 它不会阻止方法执行的线程 . 这在像WPF应用程序的UI线程这样的情况下很重要 . 挂起方法执行,一切都继续运行,阻塞线程,应用程序停止响应 .
您通常希望
async
调用一直到顶部(如事件处理程序),这允许最大的灵活性并防止死锁情况 . 然而;您可以等待Task
返回方法完成Wait
:或者获得返回值:
请注意,这两个都是同步的并阻止调用线程 . 如果异步任务正在等待调用线程上的其他一些工作完成,这样做可能会导致死锁 .
以上内容应在(3)中回答您的问题;方法本身似乎同步执行(这是
await/async
的魔力),但任务不会阻止调用线程 .它是异步,因为您不必等待方法返回 . 在您的代码中,您可以调用异步方法并将任务保存在变量中 . 继续做其他事情 . 之后,当需要方法结果时,等待响应(任务) .