C#异步编程详解

 更新时间:2017年02月06日 11:23:37   作者:东秦男人  
本文主要介绍异步编程中Task、Async和Await的基础知识。具有很好的参考价值,下面跟着小编一起来看下吧

前言

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

什么是异步?

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

异步和多线程

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

不同点:

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

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

异步应用场景及原理

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

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

举例说明:

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

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

任务

在使用任务之前,针对线程的调用大多都用线程池提供的静态方法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;
    }

除了wait等待单个任务外,task还提供了等待多个任务,WaitAny和WaitAll,它阻止调用线程,直到数组中所有的Task对象完成。

取消任务

任务的取消同样使用的是.NET Framework的标准取消操作模式,首先需要创建一个CancellationTokenSource对象,然后在函数中加入参数CancellationToken,将CancellationTokenSource的Token传递给方法,然后调用IsCancellationRequested获取是否已经取消该值进行判断。

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

任务完成后自动启动新任务

实际的开发应用中,经常出现一次任务完成后立刻启动另外一个任务,并且不能够使线程阻塞,在任务尚未完成时调用result会使程序阻塞,无法查看任务的执行进度,TASK提供了一个方法ContinueWith,它不会阻塞任何线程,当第一个任务完成时,会立即启动第二个任务。

static void Main(string[] args)
    {
      Console.WriteLine("开始进行计算");
      // ThreadPool.QueueUserWorkItem(Sum, 10);
      var ctx = new CancellationTokenSource();
      var task = new Task<int>(() => Sum(ctx.Token, 100000));
      task.Start();
      var cwt = task.ContinueWith(p =>
      {
        Console.WriteLine("task result ={0} ",task.Result);
      });
      //显示等待获取结果
      //task.Wait(ctx.Token);
      Thread.Sleep(1000);
      ctx.Cancel();
      //调用Result时,等待返回结果
      Console.WriteLine("程序结果为 Sum = {0}", task.Result);
      Console.WriteLine("程序结束");
      Console.ReadLine();
    }
    public static int Sum(CancellationToken cts, object i)
    {
      var sum = 0;    
      for (var j = 0; j <= (int)i; j++)
      {
        if (cts.IsCancellationRequested) return sum;
        Thread.Sleep(50);
        Console.Write("{0} + ", sum);
        sum += j;
      }
      Console.WriteLine(" = {0}", sum);
      return sum;
    }

Async&Await 简单使用

使用Async&Await的主要目的是方便进行异步操作,因为.net 4.0 以前进行异步操作时比较复杂的,主要是通过调用微软提供的异步回调方法进行编程,如果遇到需要自己实现的方法显得非常头疼,.net的各个版本都有自己主推的技术,像.NET1.1中的委托,.NET2.0中的泛型,.NET3.0中的Linq,.NET4.0中的Dynimac,.net4.5主推的就是异步编程,大家只需要了解TASK+异步函数就可以实现异步编程。

async:告诉CLR这是一个异步函数。

await:将Task<TResult>返回值的函数进行异步处理。

示例目的:获取网址JS代码,并在界面显示。

private static async Task<string> DownloadStringWithRetries(string uri)
    {
      using (var client = new HttpClient())
      {
        // 第1 次重试前等1 秒,第2 次等2 秒,第3 次等4 秒。
        var nextDelay = TimeSpan.FromSeconds(1);
        for (int i = 0; i != 3; ++i)
        {
          try
          {
            return await client.GetStringAsync(uri);
          }
          catch
          {
          }
          await Task.Delay(nextDelay);
          nextDelay = nextDelay + nextDelay;
        }
        // 最后重试一次,以便让调用者知道出错信息。
        return await client.GetStringAsync(uri);
      }
    }
static void Main(string[] args)
    {
      Console.WriteLine("获取百度数据");
      ExecuteAsync();
      Console.WriteLine("线程结束");
      Console.ReadLine();
    }
    public static async void ExecuteAsync()
    {
      string text = await DownloadStringWithRetries("http://wwww.baidu.com");
      Console.WriteLine(text);
    }

运行结果发现,首先获取百度数据,线程结束,最后显示HTML代码,这是因为异步开启了新的线程,并不会造成线程阻塞。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持脚本之家!

相关文章

  • C#常见应用函数实例小结

    C#常见应用函数实例小结

    这篇文章主要介绍了C#常见应用函数,结合实例形式总结分析了C#常用的时间、URL、HTML、反射、小数运算等相关函数,需要的朋友可以参考下
    2017-01-01
  • C#实现JSON解析器MojoUnityJson功能(简单且高效)

    C#实现JSON解析器MojoUnityJson功能(简单且高效)

    MojoUnityJson 是使用C#实现的JSON解析器 ,算法思路来自于游戏引擎Mojoc的C语言实现 Json.h。这篇文章主要介绍了C#实现JSON解析器MojoUnityJson的方法,需要的朋友可以参考下
    2018-01-01
  • C#开发之int与string转化操作

    C#开发之int与string转化操作

    这篇文章主要介绍了C#开发之int与string转化操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • C#实现自定义FTP操作封装类实例

    C#实现自定义FTP操作封装类实例

    这篇文章主要介绍了C#实现自定义FTP操作封装类,涉及C#操作FTP的连接、传输、下载等操作的实现技巧,非常具有实用价值,需要的朋友可以参考下
    2015-03-03
  • C#创建一个Word并打开的方法

    C#创建一个Word并打开的方法

    这篇文章主要介绍了C#创建一个Word并打开的方法,实例分析了C#操作word的常用技巧,非常具有实用价值,需要的朋友可以参考下
    2015-04-04
  • WinForm IP地址输入框控件实现

    WinForm IP地址输入框控件实现

    这篇文章主要为大家详细介绍了WinForm IP地址输入框控件的实现代码,基于VS2010模拟windows系统自带IP输入框,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05
  • C#查询SqlServer数据库并返回单个值的方法

    C#查询SqlServer数据库并返回单个值的方法

    这篇文章主要介绍了C#查询SqlServer数据库并返回单个值的方法,涉及C#操作SQLServer数据库查询的相关技巧,需要的朋友可以参考下
    2015-06-06
  • Unity实现ScrollView滑动吸附功能

    Unity实现ScrollView滑动吸附功能

    这篇文章主要为大家详细介绍了Unity实现ScrollView滑动吸附功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-09-09
  • Unity 按钮添加OnClick事件操作

    Unity 按钮添加OnClick事件操作

    这篇文章主要介绍了Unity 按钮添加OnClick事件操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • .NET连接MongoDB数据库实例教程

    .NET连接MongoDB数据库实例教程

    这则小窍门将讲述如何开发一个.NET应用来连接Mongo数据库并执行多种操作。同时还稍微涉及了Mongo数据库和多种命令
    2013-11-11

最新评论