C#异步编程详解

前言

本节主要介绍异步编程中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代码,这是因为异步开启了新的线程,并不会造成线程阻塞。

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

(0)

相关推荐

  • C#实现异步编程的方法

    最近在我参与的几个.Net项目中都有用到异步编程,作为一名.Net小白,很有必要好好地学习一下C#异步编程. 什么是异步 异步指的就是不用阻塞当前线程来等待任务的完成,而是将任务扔到线程池中去执行,当前线程可以继续向下执行,直至其它线程将任务完成,并回调通知当前线程.整个任务从开始到结束都是异步完成的,不会阻塞当前线程.因此,异步很重要的一点就是,不会阻塞当前线程. 实现异步编程 在C#语言中,主要是通过委托来实现异步编程的,在委托类型中定义了两个方法BeginInvoke()和EndInvok

  • 深入分析C#异步编程详解

    同步方法和异步方法的区别同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作异步编程概览.NET Framework 允许您异步调用任何方法.定义与您需要调用的方法具有相同签名的委托:公共语言运行库将自动为该委托定义具有适当签名的 BeginInvoke 和 EndInvoke 方法.BeginInvoke 方法用于启动异步调用.它与您需要异步执行的方法具有相同的参数,只不过还有两个额外的参数(将在稍后描述)

  • C#编程总结(六)详解异步编程

    1.什么是异步? 异步操作通常用于执行完成时间可能较长的任务,如打开大文件.连接远程计算机或查询数据库.异步操作在主应用程序线程以外的线程中执行.应用程序调用方法异步执行某个操作时,应用程序可在异步方法执行其任务时继续执行. 2.同步与异步的区别 同步(Synchronous):在执行某个操作时,应用程序必须等待该操作执行完成后才能继续执行. 异步(Asynchronous):在执行某个操作时,应用程序可在异步操作执行时继续执行.实质:异步操作,启动了新的线程,主线程与方法线程并行执行. 3.异

  • java异步编程详解

    很多时候我们都希望能够最大的利用资源,比如在进行IO操作的时候尽可能的避免同步阻塞的等待,因为这会浪费CPU的资源.如果在有可读的数据的时候能够通知程序执行读操作甚至由操作系统内核帮助我们完成数据的拷贝,这再好不过了.从NIO到CompletableFuture.Lambda.Fork/Join,java一直在努力让程序尽可能变的异步甚至拥有更高的并行度,这一点一些函数式语言做的比较好,因此java也或多或少的借鉴了某些特性.下面介绍一种非常常用的实现异步操作的方式. 考虑有一个耗时的操作,操作

  • C#异步编程详解

    前言 本节主要介绍异步编程中Task.Async和Await的基础知识. 什么是异步? 异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程. 异步和多线程 相同点:避免调用线程阻塞,从而提高软件的可响应性. 不同点: 异步操作无须额外的线程负担,并且使用回调的方式进行处理,在设计良好的情况下,处理函数可以不必使用共享变量(即使无法完全不用,最起码可以减少 共享变量的数量),减少了死锁的可能.C#5.0 .NET4.5 以后关键字Async和Awai

  • Pythony运维入门之Socket网络编程详解

    Socket是什么? Socket 是电脑网络中进程间数据流的端点Socket 是操作系统的通信机制应用程序通过Socket进行网络数据的传输 首先,简单了解一下TCP通信过程: TCP三次握手(面试常考): 第一次握手:客户端 发送SYN报文,设置随机数序号X,服务器由SYN=1知道,客户端要求建立联机 第二次握手:服务器端接收到客户端的报文之后,经过处理,返回给客户端SYN+ACK报文,同时设置随机序号Y,此时返回的报文确认ACK=X+1 第三次握手:接收到报文的客户端,会在处理确认之后,再

  • Spring中@Async注解实现异步调详解

    异步调用 在解释异步调用之前,我们先来看同步调用的定义:同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果. 异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕,继续执行下面的流程.例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法:如他们都是同步调用,则需要将他们都顺序执行完毕之后,过程才执行完毕: 如B为一个异步的调用方法,则在执行完A之后,调用B,并不等待B完成,而是执行开始调用C,待C执行完毕之后,就意味着这个过程执行完毕了. 概述说明 Sp

  • Python Socket编程详解

    背景 关于Python Socket编程,首先需要了解几个计算机网络的知识,通过以下的几个问题,有助于更好的理解Socket编程的意义,以及整个框架方面的知识: TCP和UDP协议本质上的区别? TCP协议,面向连接,可靠,基于字节流的传输层通信协议:UDP协议无连接,不可靠,基于数据包的传输层协议. TCP协议在建立连接的过程需要经历三次握手,断开连接则需要经历四次挥手,而这建立连接的过程增加了传输过程中的安全性. 而建立连接的过程则会消耗系统的资源,消耗更多的时间,而相比较UDP协议传输过程

  • Python网络编程详解

    1.服务器就是一系列硬件或软件,为一个或多个客户端(服务的用户)提供所需的"服务".它存在唯一目的就是等待客户端的请求,并响应它们(提供服务),然后等待更多请求. 2.客户端/服务器架构既可以应用于计算机硬件,也可以应用于计算机软件. 3.在服务器响应客户端之前,首先会创建一个通信节点,它能够使服务器监听请求. 一.套接字:通信端点 1.套接字 套接字是计算机网络数据结构,它体现了上节中所描述的"通信端点"的概念.在任何类型的通信开始之前,网络应用程序必须创建套接字

  • log4j2异步Logger(详解)

    1 异步Logger的意义 之前的日志框架基本都实现了AsyncAppender,被证明对性能的提升作用非常明显. 在log4j2日志框架中,增加了对Logger的异步实现.那么这一步的解耦,意义何在呢? 如图,按我目前的理解:异步Logger是让业务逻辑把日志信息放入Disruptor队列后可以直接返回(无需等待"挂载的各个Appender"都取走数据) 优点:更高吞吐.调用log方法更低的延迟. 缺点:异常处理麻烦. 可变日志消息问题.更大的CPU开销.需要等待"最慢的A

  • Python基础教程之tcp socket编程详解及简单实例

    Python tcp socket编程详解 初学脚本语言Python,测试可用的tcp通讯程序: 服务器: #!/usr/bin/env python # -*- coding: utf-8 -*- import socket import threading import time def tcplink(sock, addr): print('Accept new connection from %s:%s...' % addr); sock.send(b'Welcome!!!'); whi

  • C++ 线程(串行 并行 同步 异步)详解

    C++  线程(串行 并行 同步 异步)详解 看了很多关于这类的文章,一直没有总结.不总结的话就会一直糊里糊涂,以下描述都是自己理解的非官方语言,不一定严谨,可当作参考. 首先,进程可理解成一个可执行文件的执行过程.在ios app上的话我们可以理解为我们的app的.ipa文件执行过程也即app运行过程.杀掉app进程就杀掉了这个app在系统里运行所占的内存. 线程:线程是进程的最小单位.一个进程里至少有一个主线程.就是那个main thread.非常简单的app可能只需要一个主线程即UI线程.

随机推荐