首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何在C#中创建异步方法?

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

Stack Overflow用户
提问于 2013-04-17 23:00:51
回答 3查看 173.8K关注 0票数 209

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

代码语言:javascript
复制
private async void button1_Click(object sender, EventArgs e)
{
    var now = await CountToAsync(1000);
    label1.Text = now.ToString();
}

我写的这个方法就是CountToAsync

代码语言:javascript
复制
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**,编写异步方法的最好方式,还是应该换一种方式来编写?**

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-04-17 23:31:00

我不推荐使用StartNew,除非您需要这种级别的复杂性。

如果您的异步方法依赖于其他异步方法,最简单的方法是使用async关键字:

代码语言:javascript
复制
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;
}

如果异步方法正在执行CPU工作,则应使用Task.Run

代码语言:javascript
复制
private static async Task<DateTime> CountToAsync(int num = 10)
{
  await Task.Run(() => ...);
  return DateTime.Now;
}

你可能会发现我的async/await intro很有帮助。

票数 246
EN

Stack Overflow用户

发布于 2016-11-02 18:33:19

如果您不想在方法内部使用async/await,但仍然“修饰”它,以便能够从外部使用await关键字,则可以使用TaskCompletionSource.cs

代码语言:javascript
复制
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; 
}

From herehere

为了支持任务的这种范例,我们需要一种方法来保留任务外观和将任意异步操作引用为任务的能力,但根据提供异步的底层基础设施的规则来控制该任务的生命周期,并且以一种成本不高的方式做到这一点。这就是TaskCompletionSource的用途。

我看到它也用在.NET源代码中,比如WebClient.cs

代码语言:javascript
复制
    [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调用。因此,异步代码释放了请求线程,但只是以系统中其他地方的另一个线程为代价,对吧?没关系,您请。

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

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

Introduction to Async/Await on ASP.NET

如果目标是提高可伸缩性(而不是响应性),则完全依赖于外部I/O的存在,该I/O提供了实现该目标的机会。

票数 13
EN

Stack Overflow用户

发布于 2020-04-07 00:35:25

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

可以在异步方法中使用

Task.Yield();强制该方法异步完成。

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

代码语言:javascript
复制
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;
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16063520

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档