C#异步编程详解

2019-12-30 16:13:00王振洲

前言

本节主要介绍异步编程中Task、Async和Await的基础知识。

什么是异步?

异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程。

异步和多线程

相同点:避免调用线程阻塞,从而提高软件的可响应性。

不同点:

异步操作无须额外的线程负担,并且使用回调的方式进行处理,在设计良好的情况下,处理函数可以不必使用共享变量(即使无法完全不用,最起码可以减少 共享变量的数量),减少了死锁的可能。C#5.0 .NET4.5 以后关键字Async和Await的使用,使得异步编程变得异常简单。

多线程中的处理程序依然是顺序执行,但是多线程的缺点也同样明显,线程的使用(滥用)会给系统带来上下文切换的额外负担。并且线程间的共享变量可能造成死锁的出现。

异步应用场景及原理

异步主要应用于IO操作,数据库访问,磁盘操作,Socket访问、HTTP/TCP网络通信

原因:对于IO操作并不需要CPU进行过多的计算,这些数据主要通过磁盘进行处理,如果进行同步通信无法结束,需要创建更多的线程资源,线程的数据上下文频繁的切换也是对资源的浪费,针对IO操作不需要单独的分配一个线程来处理。

举例说明:

操作:服务器接收HTTP请求对数据库进行操作然后返回

同步处理请求的线程会被阻塞,异步处理请求的线程不会阻塞。

C#,异步编程

任务

在使用任务之前,针对线程的调用大多都用线程池提供的静态方法QueueUserWorkItem,但是这个函数有很多的限制,其中最大的问题就是没有内部机制可以让开发者知道操作在什么时候完成,也没有机制在操作完成时获取返回值,微软为了解决这个问题引入了任务的概念。

首先构造一个Task<TResult>对象,并为TResult传递返回值,开始任务之后等待它并回去结果,示例代码:


 static void Main(string[] args)
    {
      Console.WriteLine("开始进行计算");
      // ThreadPool.QueueUserWorkItem(Sum, 10);
      Task<int> task = new Task<int>(Sum, 100);
      task.Start();
      //显示等待获取结果
      task.Wait();
      //调用Result时,等待返回结果
      Console.WriteLine("程序结果为 Sum = {0}",task.Result);
      Console.WriteLine("程序结束");
      Console.ReadLine();
    }
    public static int Sum(object i)
    {
      var sum = 0;
      for (var j = 0; j <= (int) i; j++)
      {
        Console.Write("{0} + ",sum);
        sum += j;
      }
      Console.WriteLine( " = {0}",sum);
      return sum;
    }