如何在C#中创建异步方法?

如何在C#中创建异步方法?,c#,async-await,c#-5.0,C#,Async Await,C# 5.0,我读过的每一篇博文都告诉你如何使用C#中的异步方法,但出于某种奇怪的原因,我从未解释过如何构建自己的异步方法来使用。我现在有一段代码,它使用了我的方法: private async void按钮1\u单击(对象发送方,事件参数e) { var now=等待计数同步(1000); label1.Text=now.ToString(); } 我写的这个方法是CountToAsync: private Task CountToAsync(int num=1000) { 返回Task.Factory.

我读过的每一篇博文都告诉你如何使用C#中的异步方法,但出于某种奇怪的原因,我从未解释过如何构建自己的异步方法来使用。我现在有一段代码,它使用了我的方法:

private async void按钮1\u单击(对象发送方,事件参数e)
{
var now=等待计数同步(1000);
label1.Text=now.ToString();
}
我写的这个方法是
CountToAsync

private Task CountToAsync(int num=1000)
{
返回Task.Factory.StartNew(()=>
{
for(int i=0;iDateTime.Now);
}

这是使用
Task.Factory
编写异步方法的最佳方式,还是我应该用另一种方式编写?

我不建议
启动新的
,除非您需要那种复杂度

如果您的异步方法依赖于其他异步方法,最简单的方法是使用
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;
}

您可能会发现我的帮助。

如果您不想在方法内部使用async/await,但仍然“修饰”它以便能够从外部使用await关键字,请执行以下操作:

公共静态任务RunAsync(Func函数)
{ 
如果(函数==null)抛出新的ArgumentNullException(“函数”);
var tcs=new TaskCompletionSource();
ThreadPool.QueueUserWorkItem(=>
{ 
尝试
{  
T结果=函数();
tcs.SetResult(结果);
} 
catch(Exception exc){tcs.SetException(exc);}
}); 
返回tcs.Task;
}

为了用任务支持这样的范例,我们需要一种方法来保留任务外观和将任意异步操作称为任务的能力,但要根据提供异步的底层基础设施的规则来控制该任务的生命周期,而且这样做的方式不会花费太多。这就是TaskCompletionSource的目的

我看到它也在.NET源代码中使用,例如:

[主机保护(ExternalThreading=true)]
[ComVisible(false)]
公共任务上传StringTaskAsync(Uri地址、字符串方法、字符串数据)
{
//创建要返回的任务
var tcs=新任务完成源(地址);
//设置回调事件处理程序
UploadStringCompletedEventHandler=null;
handler=(sender,e)=>HandleCompletion(tcs,e,(args)=>args.Result,handler,(webClient,completion)=>webClient.UploadStringCompleted-=completion);
this.UploadStringCompleted+=处理程序;
//启动异步操作。
请尝试{this.UploadStringAsync(地址、方法、数据、tcs);}
抓住
{
this.UploadStringCompleted-=处理程序;
投掷;
}
//返回表示异步操作的任务
返回tcs.Task;
}
最后,我还发现以下内容很有用:

我一直被问这个问题。这意味着一定有某个线程阻塞了对外部资源的I/O调用。因此,异步代码释放了请求线程,但只会牺牲系统中其他地方的另一个线程,对吗?不,一点也不

为了理解异步请求扩展的原因,我将跟踪异步I/O调用的一个(简化)示例。假设一个请求需要写入一个文件。请求线程调用异步写入方法。WriteAsync由基类库(BCL)实现,并使用完成端口进行异步I/O。因此,WriteAsync调用作为异步文件写入传递给操作系统。然后,操作系统与驱动程序堆栈通信,传递数据以写入I/O请求包(IRP)

这就是事情变得有趣的地方:如果设备驱动程序不能立即处理IRP,它必须异步处理它。因此,驱动程序告诉磁盘开始写入,并向操作系统返回一个“挂起”响应。操作系统将“挂起”响应传递给BCL,BCL将不完整的任务返回给请求处理代码。请求处理代码等待任务,任务从该方法返回一个不完整的任务,依此类推。最后,请求处理代码最终将一个不完整的任务返回到ASP.NET,请求线程将被释放以返回到线程池


如果目标是提高可伸缩性(而不是响应性),那么这一切都依赖于外部I/O的存在,它提供了这样做的机会。

使方法异步的一个非常简单的方法是使用Task.Yield()方法。正如MSDN所述:

您可以使用wait Task.Yield();在异步方法中,强制 方法异步完成

在方法的开头插入它,然后它将立即返回给调用方,并在另一个线程上完成方法的其余部分

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;
}
private async Task CountToAsync(int num=1000)
{
等待任务;
for(int i=0;i
我在问一个关于如何构造方法的一般性问题。我只是想知道从哪里开始将我已经同步的方法转换成异步方法。好吧,那么一个典型的同步方法做什么,为什么要使它异步?假设我必须批处理一堆文件并返回一个结果对象。好的,那么:(1)什么是高延迟操作:获取文件——因为网络可能很慢,或者其他什么——或者进行处理——因为它是CPU密集型的,比如说。(2)您还没有说明为什么希望它首先是异步的。有没有你不想阻止的UI线程,或者什么?@EricLippert给出的例子是
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; 
}
    [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;
    }
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;
}