如何在 C# 中创建异步方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16063520/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do you create an asynchronous method in C#?
提问by Khalid Abuhakmeh
Every blog post I've read tells you how to consume an asynchronous method in C#, but for some odd reason never explain how to build your own asynchronous methods to consume. So I have this code right now that consumes my method:
我读过的每篇博文都告诉您如何在 C# 中使用异步方法,但出于某种奇怪的原因,从未解释过如何构建您自己的异步方法来使用。所以我现在有这个使用我的方法的代码:
private async void button1_Click(object sender, EventArgs e)
{
var now = await CountToAsync(1000);
label1.Text = now.ToString();
}
And I wrote this method that is CountToAsync:
我写的这个方法是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);
}
Is this, the use of Task.Factory, the best way to write an asynchronous method, or should I write this another way?
这是使用Task.Factory,编写异步方法的最佳方式,还是应该以另一种方式编写?
采纳答案by Stephen Cleary
I don't recommend StartNewunless you need that level of complexity.
StartNew除非您需要那种复杂程度,否则我不建议这样做。
If your async method is dependent on other async methods, the easiest approach is to use the asynckeyword:
如果您的异步方法依赖于其他异步方法,最简单的方法是使用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;
}
If your async method is doing CPU work, you should use Task.Run:
如果您的异步方法正在执行 CPU 工作,您应该使用Task.Run:
private static async Task<DateTime> CountToAsync(int num = 10)
{
await Task.Run(() => ...);
return DateTime.Now;
}
You may find my async/awaitintrohelpful.
您可能会发现我的async/await介绍很有帮助。
回答by Alberto
If you didn't want to use async/await inside your method, but still "decorate" it so as to be able to use the await keyword from outside, TaskCompletionSource.cs:
如果你不想在你的方法中使用 async/await,但仍然“装饰”它以便能够从外部使用 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;
}
To support such a paradigm with Tasks, we need a way to retain the Task fa?ade and the ability to refer to an arbitrary asynchronous operation as a Task, but to control the lifetime of that Task according to the rules of the underlying infrastructure that's providing the asynchrony, and to do so in a manner that doesn't cost significantly. This is the purpose of TaskCompletionSource.
为了支持这样的任务范式,我们需要一种方法来保留任务外观和将任意异步操作称为任务的能力,但要根据底层基础设施的规则来控制该任务的生命周期提供异步性,并以不显着成本的方式进行。这就是 TaskCompletionSource 的目的。
I saw is also used in the .NET source eg. WebClient.cs:
我看到也用于 .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;
}
Finally, I found useful also the following:
最后,我发现以下内容也很有用:
I get asked this question all the time. The implication is that there must be some thread somewhere that's blocking on the I/O call to the external resource. So, asynchronous code frees up the request thread, but only at the expense of another thread elsewhere in the system, right? No, not at all. To understand why asynchronous requests scale, I'll trace a (simplified) example of an asynchronous I/O call. Let's say a request needs to write to a file. The request thread calls the asynchronous write method. WriteAsync is implemented by the Base Class Library (BCL), and uses completion ports for its asynchronous I/O. So, the WriteAsync call is passed down to the OS as an asynchronous file write. The OS then communicates with the driver stack, passing along the data to write in an I/O request packet (IRP). This is where things get interesting: If a device driver can't handle an IRP immediately, it must handle it asynchronously. So, the driver tells the disk to start writing and returns a “pending” response to the OS. The OS passes that “pending” response to the BCL, and the BCL returns an incomplete task to the request-handling code. The request-handling code awaits the task, which returns an incomplete task from that method and so on. Finally, the request-handling code ends up returning an incomplete task to ASP.NET, and the request thread is freed to return to the thread pool.
我一直被问到这个问题。这意味着一定有某个线程阻塞了对外部资源的 I/O 调用。所以,异步代码释放了请求线程,但只是以系统中其他地方的另一个线程为代价,对吗?一点都不。为了理解为什么异步请求会扩展,我将跟踪一个异步 I/O 调用的(简化的)示例。假设一个请求需要写入一个文件。请求线程调用异步写入方法。WriteAsync 由基类库 (BCL) 实现,并为其异步 I/O 使用完成端口。因此,WriteAsync 调用作为异步文件写入传递给操作系统。然后操作系统与驱动程序堆栈通信,将数据传递到 I/O 请求包 (IRP) 中写入。这就是事情变得有趣的地方:如果设备驱动程序不能立即处理 IRP,它必须异步处理它。因此,驱动程序告诉磁盘开始写入并向操作系统返回“待处理”响应。操作系统将该“待处理”响应传递给 BCL,BCL 将未完成的任务返回给请求处理代码。请求处理代码等待任务,它从该方法返回一个未完成的任务,依此类推。最后,请求处理代码最终将一个未完成的任务返回给 ASP.NET,请求线程被释放以返回线程池。BCL 向请求处理代码返回一个未完成的任务。请求处理代码等待任务,它从该方法返回一个未完成的任务,依此类推。最后,请求处理代码最终将一个未完成的任务返回给 ASP.NET,请求线程被释放以返回线程池。BCL 向请求处理代码返回一个未完成的任务。请求处理代码等待任务,它从该方法返回一个未完成的任务,依此类推。最后,请求处理代码最终将一个未完成的任务返回给 ASP.NET,并且请求线程被释放以返回线程池。
Introduction to Async/Await on ASP.NET
If the target is to improve scalability (rather than responsiveness), it all relies on the existence of an external I/O that provides the opportunity to do that.
如果目标是提高可扩展性(而不是响应能力),那么这一切都依赖于提供实现这一目标的外部 I/O 的存在。
回答by SalgoMato
One very simple way to make a method asynchronous is to use Task.Yield() method. As MSDN states:
使方法异步的一种非常简单的方法是使用 Task.Yield() 方法。正如 MSDN 所说:
You can use await Task.Yield(); in an asynchronous method to force the method to complete asynchronously.
您可以使用 await Task.Yield(); 在异步方法中强制方法异步完成。
Insert it at beginning of your method and it will then return immediately to the caller and complete the rest of the method on another thread.
在方法的开头插入它,然后它会立即返回给调用者并在另一个线程上完成方法的其余部分。
private async Task<DateTime> CountToAsync(int num = 1000)
{
await Task.Yield();
for (int i = 0; i < num; i++)
{
Console.WriteLine("#{0}", i);
}
return DateTime.Now;
}

