How and when to use ‘async’ and ‘await’
据我所知,
我目前正在尝试最基本的例子。我已经在网上添加了一些评论。你能为我澄清一下吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // I don't understand why this method must be marked as `async`. private async void button1_Click(object sender, EventArgs e) { Task<int> access = DoSomethingAsync(); // task independent stuff here // this line is reached after the 5 seconds sleep from // DoSomethingAsync() method. Shouldn't it be reached immediately? int a = 1; // from my understanding the waiting should be done here. int x = await access; } async Task<int> DoSomethingAsync() { // is this executed on a background thread? System.Threading.Thread.Sleep(5000); return 1; } |
当使用
下面是一个例子,希望我能解释一些正在进行的高级细节:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public async Task MyMethodAsync() { Task<int> longRunningTask = LongRunningOperationAsync(); // independent work which doesn't need the result of LongRunningOperationAsync can be done here //and now we call await on the task int result = await longRunningTask; //use the result Console.WriteLine(result); } public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation { await Task.Delay(1000); // 1 second delay return 1; } |
好吧,那么这里发生了什么:
我们假设主线程(线程ID=1)已经到达
现在,如果
第二种情况是,
关于其他答案,请看一下等待(参考文献)
更具体地说,在包含的示例中,它解释了您的情况
The following Windows Forms example illustrates the use of await in an
async method, WaitAsynchronouslyAsync. Contrast the behavior of that
method with the behavior of WaitSynchronously. Without an await
operator applied to a task, WaitSynchronously runs synchronously
despite the use of the async modifier in its definition and a call to
Thread.Sleep in its body.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | private async void button1_Click(object sender, EventArgs e) { // Call the method that runs asynchronously. string result = await WaitAsynchronouslyAsync(); // Call the method that runs synchronously. //string result = await WaitSynchronously (); // Display the result. textBox1.Text += result; } // The following method runs asynchronously. The UI thread is not // blocked during the delay. You can move or resize the Form1 window // while Task.Delay is running. public async Task<string> WaitAsynchronouslyAsync() { await Task.Delay(10000); return"Finished"; } // The following method runs synchronously, despite the use of async. // You cannot move or resize the Form1 window while Thread.Sleep // is running because the UI thread is blocked. public async Task<string> WaitSynchronously() { // Add a using directive for System.Threading. Thread.Sleep(10000); return"Finished"; } |
From my understanding one of the main things that async and await do is to make code easy to write and read.
它们将使异步代码易于编写和读取,是的。
Is it the same thing as spawning background threads to perform long duration logic?
一点也不。
// I don't understand why this method must be marked as 'async'.
// This line is reached after the 5 seconds sleep from DoSomethingAsync() method. Shouldn't it be reached immediately?
不,因为默认情况下,
// Is this executed on a background thread?
不。
你可能会发现我的
解释
下面是一个高级异步/等待的快速示例。除此之外还有很多细节需要考虑。
注:
在下面的示例中,第一个块正是这样做的。它立即开始所有任务(
在下面的示例中,第二个块正在启动一个任务,并等待它完成(这是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | Console.WriteLine(DateTime.Now); // This block takes 1 second to run because all // 5 tasks are running simultaneously { var a = Task.Delay(1000); var b = Task.Delay(1000); var c = Task.Delay(1000); var d = Task.Delay(1000); var e = Task.Delay(1000); await a; await b; await c; await d; await e; } Console.WriteLine(DateTime.Now); // This block takes 5 seconds to run because each"await" // pauses the code until the task finishes { await Task.Delay(1000); await Task.Delay(1000); await Task.Delay(1000); await Task.Delay(1000); await Task.Delay(1000); } Console.WriteLine(DateTime.Now); |
输出:
1 2 3 | 5/24/2017 2:22:50 PM 5/24/2017 2:22:51 PM (First block took 1 second) 5/24/2017 2:22:56 PM (Second block took 5 seconds) |
有关同步上下文的额外信息
注意:这是事情对我来说有点模糊的地方,所以如果我有任何错误,请纠正我,我会更新答案。对这项工作有一个基本的了解是很重要的,但只要你从不使用
这其中有一个方面使得异步/等待概念更难理解。这就是在本例中,所有这些都发生在同一个线程上的事实(或者至少是在同步上下文中看起来是同一个线程的情况)。默认情况下,
你是怎么做到的?默认情况下,
1 2 | await a; //Same as the line below await a.ConfigureAwait(true); |
如果您想让主代码在没有原始上下文的情况下在新线程上继续运行,只需使用false而不是true,这样它就知道不需要恢复上下文。
1 | await a.ConfigureAwait(false); |
程序暂停完成后,它可能会在具有不同上下文的完全不同的线程上继续运行。这就是性能改进的来源——它可以在任何可用线程上继续运行,而不必恢复它开始时的原始上下文。
这东西令人困惑吗?该死!你能弄明白吗?可能!一旦你掌握了这些概念,那么继续阅读斯蒂芬·克利里的解释,它更倾向于对异步/等待有技术理解的人。
在一个简单的控制台程序中显示上述说明-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Program { static void Main(string[] args) { TestAsyncAwaitMethods(); Console.WriteLine("Press any key to exit..."); Console.ReadLine(); } public async static void TestAsyncAwaitMethods() { await LongRunningMethod(); } public static async Task<int> LongRunningMethod() { Console.WriteLine("Starting Long Running method..."); await Task.Delay(5000); Console.WriteLine("End Long Running method..."); return 1; } } |
输出为:
1 2 3 | Starting Long Running method... Press any key to exit... End Long Running method... |
因此,
因此,没有线程被阻塞。
我认为你在
阅读本文,我认为这是对
这里有一个快速的控制台程序,可以让后面的人明白。"tasktodo"方法是您希望使其成为异步的长时间运行的方法。让它运行async是由testasync方法完成的。测试循环方法只在"tasktodo"任务中运行,并以异步方式运行它们。您可以在结果中看到这一点,因为它们在从运行到运行的过程中并没有以相同的顺序完成—它们完成后会向控制台UI线程报告。简单化,但我认为简单化的例子比更复杂的例子更能说明模式的核心:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace TestingAsync { class Program { static void Main(string[] args) { TestLoops(); Console.Read(); } private static async void TestLoops() { for (int i = 0; i < 100; i++) { await TestAsync(i); } } private static Task TestAsync(int i) { return Task.Run(() => TaskToDo(i)); } private async static void TaskToDo(int i) { await Task.Delay(10); Console.WriteLine(i); } } } |
此答案旨在提供一些特定于ASP.NET的信息。
通过在MVC控制器中使用Async/Await,可以提高线程池利用率并获得更好的吞吐量,如下面的文章所述,
http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-asp net-mvc-4
In web applications that sees a large number of concurrent requests at
start-up or has a bursty load (where concurrency increases suddenly),
making these web service calls asynchronous will increase the
responsiveness of your application. An asynchronous request takes the
same amount of time to process as a synchronous request. For example,
if a request makes a web service call that requires two seconds to
complete, the request takes two seconds whether it is performed
synchronously or asynchronously. However, during an asynchronous call,
a thread is not blocked from responding to other requests while it
waits for the first request to complete. Therefore, asynchronous
requests prevent request queuing and thread pool growth when there are
many concurrent requests that invoke long-running operations.
这里的所有答案都使用task.delay()或其他内置异步函数。但我的示例不使用这些异步函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | // Starts counting to a large numbewr and then immediately displays message"i'm counting...". // Then it waits for task to finish and displays"finished, press any key". static void asyncTest () { Console.WriteLine("Started asyncTest()"); Task<long> task = asyncTest_count(); Console.WriteLine("Started counting, please wait..."); task.Wait(); // if you comment this line you will see that message"Finished counting" will be displayed before we actually finished counting. //Console.WriteLine("Finished counting to" + task.Result.ToString()); // using task.Result seems to also call task.Wait(). Console.WriteLine("Finished counting."); Console.WriteLine("Press any key to exit program."); Console.ReadLine(); } static async Task<long> asyncTest_count() { long k = 0; Console.WriteLine("Started asyncTest_count()"); await Task.Run(() => { long countTo = 100000000; int prevPercentDone = -1; for (long i = 0; i <= countTo; i++) { int percentDone = (int)(100 * (i / (double)countTo)); if (percentDone != prevPercentDone) { prevPercentDone = percentDone; Console.Write(percentDone.ToString() +"%"); } k = i; } }); Console.WriteLine(""); Console.WriteLine("Finished asyncTest_count()"); return k; } |
老实说,我仍然认为最好的解释是关于未来和维基百科上的承诺:http://en.wikipedia.org/wiki/futures_and_promises
基本思想是,您有一个单独的线程池,用于异步执行任务。使用时。但是,对象确实承诺它将在某个时间执行该操作,并在您请求时提供结果。这意味着它将在您请求结果时阻塞,并且还没有完成,但是在线程池中执行。
从那里你可以优化事情:一些操作可以异步实现,你可以通过批处理随后的请求和/或重新排序来优化诸如文件IO和网络通信之类的事情。我不确定这是否已经在微软的任务框架中——但如果不是,那将是我要添加的第一件事。
实际上,您可以在C 4.0中使用收益率实现未来的模式。如果你想知道它是如何工作的,我可以推荐这个链接做一个体面的工作:http://code.google.com/p/fracture/source/browse/trunk/squared/tasklib/。然而,如果你自己开始玩弄它,你会发现如果你想做所有的酷事情,你真的需要语言支持——这正是微软所做的。
请参阅此fiddle https://dotnetfiddle.net/vhzdlu(并在可能的情况下进行改进),以运行一个简单的控制台应用程序,该应用程序显示同一程序中任务、task.waitall()、async和wait运算符的用法。
这个小提琴应该可以清除您的执行周期概念。
这是样本代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | using System; using System.Threading.Tasks; public class Program { public static void Main() { var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion"); Console.WriteLine("Now Waiting for Task to be Finished"); Task.WaitAll(a); //Now Waiting Console.WriteLine("Exiting CommandLine"); } public static async Task MyMethodAsync() { Task<int> longRunningTask = LongRunningOperation(); // independent work which doesn't need the result of LongRunningOperationAsync can be done here Console.WriteLine("Independent Works of now executes in MyMethodAsync()"); //and now we call await on the task int result = await longRunningTask; //use the result Console.WriteLine("Result of LongRunningOperation() is" + result); } public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation { Console.WriteLine("LongRunningOperation() Started"); await Task.Delay(2000); // 2 second delay Console.WriteLine("LongRunningOperation() Finished after 2 Seconds"); return 1; } } |
来自输出窗口的跟踪:
异步/等待
实际上,async/await是一对关键字,它们只是创建异步任务回调的语法甜头。
以这个操作为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public static void DoSomeWork() { var task = Task.Run(() => { // [RUNS ON WORKER THREAD] // IS NOT bubbling up due to the different threads throw new Exception(); Thread.Sleep(2000); return"Hello"; }); // This is the callback task.ContinueWith((t) => { // -> Exception is swallowed silently Console.WriteLine("Completed"); // [RUNS ON WORKER THREAD] }); } |
上面的代码有几个缺点。错误不会传递下去,而且很难读取。但是异步和等待来帮助我们:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public async static void DoSomeWork() { var result = await Task.Run(() => { // [RUNS ON WORKER THREAD] // IS bubbling up throw new Exception(); Thread.Sleep(2000); return"Hello"; }); // every thing below is a callback // (including the calling methods) Console.WriteLine("Completed"); } |
等待调用必须是异步方法。这有一些优势:
- 返回任务的结果
- 自动创建回调
- 检查错误并让它们在调用堆栈中冒泡(在调用堆栈中最多不等待调用)
- 等待结果
- 释放主线程
- 在主线程上运行回调
- 为任务使用线程池中的工作线程
- 使代码易于阅读
- 还有很多
注意:Async和Await与异步调用一起使用,不进行这些调用。您必须为此使用任务库,如task.run()。
下面是等待和无等待解决方案之间的比较
这是非异步解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public static long DoTask() { stopWatch.Reset(); stopWatch.Start(); // [RUNS ON MAIN THREAD] var task = Task.Run(() => { Thread.Sleep(2000); // [RUNS ON WORKER THREAD] }); // goes directly further // WITHOUT waiting until the task is finished // [RUNS ON MAIN THREAD] stopWatch.Stop(); // 50 milliseconds return stopWatch.ElapsedMilliseconds; } |
这是异步方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public async static Task<long> DoAwaitTask() { stopWatch.Reset(); stopWatch.Start(); // [RUNS ON MAIN THREAD] await Task.Run(() => { Thread.Sleep(2000); // [RUNS ON WORKER THREAD] }); // Waits until task is finished // [RUNS ON MAIN THREAD] stopWatch.Stop(); // 2050 milliseconds return stopWatch.ElapsedMilliseconds; } |
实际上,您可以在不使用await关键字的情况下调用异步方法,但这意味着这里的任何异常都会在发布模式中被忽略:
1 2 3 4 5 6 7 8 9 10 | public static Stopwatch stopWatch { get; } = new Stopwatch(); static void Main(string[] args) { Console.WriteLine("DoAwaitTask:" + DoAwaitTask().Result +" ms"); // 2050 (2000 more because of the await) Console.WriteLine("DoTask:" + DoTask() +" ms"); // 50 Console.ReadKey(); } |
异步和等待不适用于并行计算。它们用于不阻塞您的主线程。当涉及到ASP.NET或Windows应用程序时,由于网络调用而阻塞主线程是一件坏事。如果你这样做,你的应用程序将失去响应,甚至崩溃。
查看MS文档了解更多示例。
为了最快的学习……好的。
了解方法执行流程(带图表):3分钟好的。
问题反省(学习清酒):1分钟好的。
快速完成语法:5分钟好的。
分享开发者的困惑:5分钟好的。
问题:快速将正常代码的实际实现更改为异步代码:2分钟好的。
下一步该怎么办?好的。
了解方法执行流程(带图表):3分钟好的。
在这张图片中,只需关注6好的。
在6步骤:accessTheWebAsync()已经耗尽了它在没有getStringTask结果的情况下可以完成的工作。因此,accessthewebasync使用一个wait操作符来暂停其进程并将控制权(yield)返回给调用者。accessthewebasync向调用者返回一个任务(字符串返回值)。任务表示产生字符串结果的承诺。但是什么时候回电话呢?又打了第二个电话?好的。
accessTheWebAsync()的调用者只做了等待(它可以完成一些内部任务,然后在需要时等待)。因此,调用方正在等待访问WebAsync,而AccessTheWebAsync正在等待GetStringAsync。好的。
记住,方法已经返回,不能再次返回(第二次)。那么打电话的人怎么知道呢?一切都与任务有关!任务已返回。任务已等待(不是方法,不是值)。值将在任务中设置。任务状态将设置为"完成"。调用者只监视任务。以后再看。好的。
为了学习而反省问题:1分钟好的。
让我们稍微调整一下这个问题:好的。
How and When to use
async andawait Tasks ?Ok.
因为学习
快速完成语法:5分钟好的。
转换前(原始方法)好的。
internal static int Method(int arg0, int arg1) 好的。
{
int result = arg0 + arg1;
IO(); // Do some long running IO.
return result;
}调用上述方法的另一个任务化方法好的。
internal static Task 好的。MethodTask(int arg0, int arg1)
{
Tasktask = new Task (() => Method(arg0, arg1));
task.Start(); // Hot task (started task) should always be returned.
return task;
}
我们提到了等待还是异步?不,调用上面的方法,就得到一个任务。你可以监控。你已经知道任务返回了什么。整数。好的。
调用任务有点棘手。让我们调用methodtask()。好的。
internal static async Task 好的。MethodAsync(int arg0, int arg1)
{
int result = await HelperMethods.MethodTask(arg0, arg1);
return result;
}
我们正在等待任务完成。因此出现了
分享开发者的困惑:5分钟好的。
开发人员犯了一个错误,没有实现
提示:尝试找到一个现有的异步实现(如
问题:快速将正常代码的实际实现更改为异步操作:2分钟好的。
下面数据层中显示的代码行开始中断(很多地方)。因为我们将一些代码从.NET Framework 4.2更新到了.NET核心。我们必须在整个应用程序的1小时内解决这个问题!好的。
1 | var myContract = query.Where(c => c.ContractID == _contractID).First(); |
轻松愉快!好的。
代码是这样改变的好的。
1 | var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync(); |
方法签名已从更改好的。
到好的。
调用方法也受到影响:
我们在30分钟内改变了它!好的。
但是架构师告诉我们不要仅仅为了这个而使用EntityFramework库!哎呀!戏剧!然后我们做了一个定制的任务实现。你知道怎么做。仍然容易!好的。
下一步该怎么办?在ASP.NET核心中,有一个非常好的关于将同步调用转换为异步调用的快速视频,因为这很可能是阅读本文之后的发展方向。好的。好啊。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public static void Main(string[] args) { string result = DownloadContentAsync().Result; Console.ReadKey(); } // You use the async keyword to mark a method for asynchronous operations. // The"async" modifier simply starts synchronously the current thread. // What it does is enable the method to be split into multiple pieces. // The boundaries of these pieces are marked with the await keyword. public static async Task<string> DownloadContentAsync()// By convention, the method name ends with"Async { using (HttpClient client = new HttpClient()) { // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished. // If it is already finished, the method continues to run synchronously. // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed. // Http request example. // (In this example I can set the milliseconds after"sleep=") String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000"); Console.WriteLine(result); // After completing the result response, the state machine will continue to synchronously execute the other processes. return result; } } |
更高层次:
1)async关键字启用await,这就是它所做的一切。Async关键字不在单独的线程中运行该方法。开始的f async方法同步运行,直到它命中等待一个耗时的任务为止。
2)您可以等待返回t类型的任务或任务的方法。您不能等待异步void方法。
3)当主线程遇到等待耗时任务或实际工作启动时,主线程将返回到当前方法的调用方。
4)如果主线程看到一个仍在执行的任务的等待,它不会等待它并返回到当前方法的调用方。这样,应用程序就可以保持响应。
5)等待处理任务,现在将在线程池的单独线程上执行。
6)当这个等待任务完成时,下面的所有代码将由单独的线程执行。
下面是示例代码。执行并检查线程ID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | using System; using System.Threading; using System.Threading.Tasks; namespace AsyncAwaitDemo { class Program { public static async void AsynchronousOperation() { Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id:" + Thread.CurrentThread.ManagedThreadId); //Task<int> _task = AsyncMethod(); int count = await AsyncMethod(); Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id:" + Thread.CurrentThread.ManagedThreadId); //int count = await _task; Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id:" + Thread.CurrentThread.ManagedThreadId); DependentMethod(count); Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id:" + Thread.CurrentThread.ManagedThreadId); } public static async Task<int> AsyncMethod() { Console.WriteLine("Inside AsyncMethod, Thread Id:" + Thread.CurrentThread.ManagedThreadId); int count = 0; await Task.Run(() => { Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id:" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(20000); count = 10; }); Console.WriteLine("Completed AsyncMethod, Thread Id:" + Thread.CurrentThread.ManagedThreadId); return count; } public static void DependentMethod(int count) { Console.WriteLine("Inside DependentMethod, Thread Id:" + Thread.CurrentThread.ManagedThreadId +". Total count is" + count); } static void Main(string[] args) { Console.WriteLine("Started Main method, Thread Id:" + Thread.CurrentThread.ManagedThreadId); AsynchronousOperation(); Console.WriteLine("Completed Main method, Thread Id:" + Thread.CurrentThread.ManagedThreadId); Console.ReadKey(); } } } |
异步等待简单解释
简单类比法
一个人可以等早班火车。这就是他们所做的一切,因为这是他们目前正在执行的主要任务。(同步编程(你通常做的!))
另一个人可能在等早班火车,一边抽烟,一边喝咖啡。(异步编程)
什么是异步编程?
异步编程是一个程序员选择在一个独立的线程上运行他的一些代码,而不是在执行的主线程上运行,然后在主线程完成时通知主线程。
async关键字实际上做了什么?
将async关键字前缀为
1 | async void DoSomething(){ . . . |
允许程序员在调用异步任务时使用await关键字。就这样。
为什么这很重要?
在许多软件系统中,主线程是为专门与用户界面相关的操作而保留的。如果我在我的计算机上运行一个非常复杂的递归算法,需要5秒钟才能完成,但是我在主线程(UI线程)上运行这个算法,当用户试图单击我的应用程序上的任何内容时,它似乎被冻结,因为我的主线程已经排队,并且当前正在处理太多的操作。因此,主线程无法处理鼠标单击以从按钮单击运行方法。
你什么时候用异步等待?
当您做任何不涉及用户界面的事情时,最好使用异步关键字。
所以假设你正在编写一个程序,允许用户在手机上绘制草图,但每隔5秒钟,它就会在互联网上检查天气。
我们应该等待每5秒一次的网络投票呼叫来获取天气,因为应用程序的用户需要与移动触摸屏保持交互以绘制漂亮的图片。
如何使用异步和等待
下面是上面的例子,下面是一些如何编写它的伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | //ASYNCHRONOUS //this is called every 5 seconds async void CheckWeather() { var weather = await GetWeather(); //do something with the weather now you have it } async Task<WeatherResult> GetWeather() { var weatherJson = await CallToNetworkAddressToGetWeather(); return deserializeJson<weatherJson>(weatherJson); } //SYNCHRONOUS //This method is called whenever the screen is pressed void ScreenPressed() { DrawSketchOnScreen(); } |
据我所知,还应该在组合中添加第三个术语:
你是一个任务。当代码执行到达这一行时,控件跳回到周围原始函数的调用方。
相反,如果将
在以下代码中,httpclient方法getbytearrayasync返回一个任务getContentStack。该任务承诺在任务完成时生成实际的字节数组。wait运算符应用于getContentStack,以在sumPageSizesAsync中暂停执行,直到getContentStack完成。同时,控件返回给sumpagesizesasync的调用方。当getContentStack完成时,await表达式的计算结果为字节数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 | private async Task SumPageSizesAsync() { // To use the HttpClient type in desktop apps, you must include a using directive and add a // reference for the System.Net.Http namespace. HttpClient client = new HttpClient(); // . . . Task<byte[]> getContentsTask = client.GetByteArrayAsync(url); byte[] urlContents = await getContentsTask; // Equivalently, now that you see how it works, you can write the same thing in a single line. //byte[] urlContents = await client.GetByteArrayAsync(url); // . . . } |
is using them equal to spawning background threads to perform long
duration logic?
本文mdsn:Async and Await(c)的异步编程明确地解释了这一点:
The async and await keywords don't cause additional threads to be
created. Async methods don't require multithreading because an async
method doesn't run on its own thread. The method runs on the current
synchronization context and uses time on the thread only when the
method is active.
这里的答案对于有关等待/异步的一般指导很有用。它们还包含一些有关如何连接等待/异步的详细信息。在使用这个设计模式之前,我想和您分享一些您应该知道的实践经验。
术语"wait"是字面上的,因此无论您在哪个线程上调用它,都将在继续之前等待方法的结果。在前台线程上,这是一个灾难。前台线程承担了构建应用程序的负担,包括视图、视图模型、初始动画以及用这些元素引导的其他内容。所以当你等待前台线程时,你就停止了应用程序。当没有任何事情发生时,用户会等待。这提供了负面的用户体验。
您当然可以使用各种方法等待后台线程:
1 2 3 4 5 6 7 8 9 10 | Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); }); // Notice that we do not await the following call, // as that would tie it to the foreground thread. try { Task.Run(async () => { await AnyAwaitableMethod(); }); } catch {} |
这些备注的完整代码位于https://github.com/marcusts/xamarin-forms-烦扰。请参阅名为awaiAsyncAntpattern.sln的解决方案。
Github站点还提供了指向有关此主题的更详细讨论的链接。
下面是通过打开对话框读取Excel文件的代码,然后使用Async和Wait来异步运行从Excel逐行读取并绑定到网格的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | namespace EmailBillingRates { public partial class Form1 : Form { public Form1() { InitializeComponent(); lblProcessing.Text =""; } private async void btnReadExcel_Click(object sender, EventArgs e) { string filename = OpenFileDialog(); Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application(); Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename); Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1]; Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange; try { Task<int> longRunningTask = BindGrid(xlRange); int result = await longRunningTask; } catch (Exception ex) { MessageBox.Show(ex.Message.ToString()); } finally { //cleanup // GC.Collect(); //GC.WaitForPendingFinalizers(); //rule of thumb for releasing com objects: // never use two dots, all COM objects must be referenced and released individually // ex: [somthing].[something].[something] is bad //release com objects to fully kill excel process from running in the background Marshal.ReleaseComObject(xlRange); Marshal.ReleaseComObject(xlWorksheet); //close and release xlWorkbook.Close(); Marshal.ReleaseComObject(xlWorkbook); //quit and release xlApp.Quit(); Marshal.ReleaseComObject(xlApp); } } private void btnSendEmail_Click(object sender, EventArgs e) { } private string OpenFileDialog() { string filename =""; OpenFileDialog fdlg = new OpenFileDialog(); fdlg.Title ="Excel File Dialog"; fdlg.InitialDirectory = @"c:"; fdlg.Filter ="All files (*.*)|*.*|All files (*.*)|*.*"; fdlg.FilterIndex = 2; fdlg.RestoreDirectory = true; if (fdlg.ShowDialog() == DialogResult.OK) { filename = fdlg.FileName; } return filename; } private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange) { lblProcessing.Text ="Processing File.. Please wait"; int rowCount = xlRange.Rows.Count; int colCount = xlRange.Columns.Count; // dt.Column = colCount; dataGridView1.ColumnCount = colCount; dataGridView1.RowCount = rowCount; for (int i = 1; i <= rowCount; i++) { for (int j = 1; j <= colCount; j++) { //write the value to the Grid if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null) { await Task.Delay(1); dataGridView1.Rows[i - 1].Cells[j - 1].Value = xlRange.Cells[i, j].Value2.ToString(); } } } lblProcessing.Text =""; return 0; } } internal class async { } } |