你如何在C#中创build一个asynchronous方法?
我读过的每篇博文都告诉你如何在C#中使用一个asynchronous方法,但由于某种奇怪的原因,从来没有解释如何构build自己的asynchronous方法来使用。 所以我现在有这个代码消耗我的方法:
private async void button1_Click(object sender, EventArgs e) { var now = await CountToAsync(1000); label1.Text = now.ToString(); }
而我写的这个方法就是CountToAsync
:
private Task<DateTime> CountToAsync(int num = 1000) { return Task.Factory.StartNew(() => { for (int i = 0; i < num; i++) { Console.WriteLine("#{0}", i); } }).ContinueWith(x => DateTime.Now); }
是这样,使用Task.Factory
,写一个asynchronous方法的最好方法,或者我应该以另一种方式写这个?
除非您需要这种复杂性,否则我不build议使用StartNew
。
如果你的asynchronous方法依赖于其他的asynchronous方法,最简单的方法是使用async
关键字:
private static async Task<DateTime> CountToAsync(int num = 10) { for (int i = 0; i < num; i++) { await Task.Delay(TimeSpan.FromSeconds(1)); } return DateTime.Now; }
如果你的asynchronous方法在做CPU工作,你应该使用Task.Run
:
private static async Task<DateTime> CountToAsync(int num = 10) { await Task.Run(() => ...); return DateTime.Now; }
你可能会发现我的async
/ await
介绍有帮助。
如果你不想在你的方法中使用asynchronous/等待,但仍然“装饰”它,以便能够从外部使用await关键字, TaskCompletionSource.cs :
public static Task<T> RunAsync<T>(Func<T> function) { if (function == null) throw new ArgumentNullException(“function”); var tcs = new TaskCompletionSource<T>(); ThreadPool.QueueUserWorkItem(_ => { try { T result = function(); tcs.SetResult(result); } catch(Exception exc) { tcs.SetException(exc); } }); return tcs.Task; }
从这里到这里
为了支持这样一个任务模式,我们需要一种方法来保留任务外观和能够将任意asynchronous操作称为任务,但是要根据提供的基础架构的规则来控制该任务的生命周期不同步,并且这样做的成本并不显着。 这是TaskCompletionSource的目的。
我看到也在.NET源代码中使用。 WebClient.cs :
[HostProtection(ExternalThreading = true)] [ComVisible(false)] public Task<string> UploadStringTaskAsync(Uri address, string method, string data) { // Create the task to be returned var tcs = new TaskCompletionSource<string>(address); // Setup the callback event handler UploadStringCompletedEventHandler handler = null; handler = (sender, e) => HandleCompletion(tcs, e, (args) => args.Result, handler, (webClient, completion) => webClient.UploadStringCompleted -= completion); this.UploadStringCompleted += handler; // Start the async operation. try { this.UploadStringAsync(address, method, data, tcs); } catch { this.UploadStringCompleted -= handler; throw; } // Return the task that represents the async operation return tcs.Task; }
最后,我发现有用的还有以下几点:
我总是被问到这个问题。 其含义是在某个地方必须有某个线程阻塞在对外部资源的I / O调用上。 所以,asynchronous代码释放了请求线程,但只是牺牲了系统中其他线程的代价,对吧? 一点都不。 为了理解asynchronous请求扩展的原因,我将跟踪一个asynchronousI / O调用的(简化的)例子。 假设一个请求需要写入一个文件。 请求线程调用asynchronous写入方法。 WriteAsync由基类库(BCL)实现,并为其asynchronousI / O使用完成端口。 因此,WriteAsync调用作为asynchronous文件写入传递到操作系统。 OS然后与驱动程序堆栈进行通信,传递数据以写入I / O请求包(IRP)。 这是事情变得有趣的地方:如果设备驱动程序不能立即处理一个IRP,它必须asynchronous处理它。 所以,驱动程序会告诉磁盘开始写入,并向操作系统返回“待处理”响应。 操作系统将这个“挂起”响应传递给BCL,并且BCL将不完整的任务返回给请求处理代码。 请求处理代码等待该任务,该任务从该方法返回一个不完整的任务,等等。 最后,请求处理代码最终返回一个不完整的任务到ASP.NET,并且请求线程被释放返回到线程池。
ASP.NET上的Async / Await简介
如果目标是提高可伸缩性(而不是响应能力),则所有这些都依赖于提供机会的外部I / O的存在。
只需在代码中添加几个字 – “asynchronous”和“等待”:
private async Task<DateTime> CountToAsync(int num = 1000) { return await Task.Factory.StartNew(() => { for (int i = 0; i < num; i++) { Console.WriteLine("#{0}", i); } }).ContinueWith(x => DateTime.Now); }