C#常用异步方法调用(1)_Async/Await特性详细总结



前言

在总结之前需要了解什么是同步方法、异步方法

同步方法:程序按照编写顺序进行执行,在调用方法时线程会等待方法返回后再继续执行下一步;
异步方法:与同步方法相反,程序在执行方法时在方法尚未完成就返回调用方法继续执行,在调用方法继续执行的时候等待异步方法完成。

异步方法常用的几种方式:
1、Async/Await 特性
2、Bachground 类
3、Task Parallet library 并行库

为什么需要用异步方法,异步方法有什么好处?

在实际应用过程中当程序执行代码时如果按照顺序一个个执行语句并等待结束并没有什么问题,但当执行个别耗时比较大的语句时程序依旧等待语句结束再执行之后的语句,这样就导致用户的等待的时间过多并且如果是处理UI界面时用户对界面的操作只能等待语句结束时才可能被响应,这种情况我们可以把耗时比较长的语句在新的线程里执行而主线程可以继续响应之后的语句及事件,这样就能提高整个程序的效率以及用户的体验感。


本次要总结的为Async/Await 类

一、Async/Await特性语法结构

1. 异步调用方法:异步方法的调用,其返回类型有三种:

	 - Void:无返回类型
	 - Task:能够返回Task类型可以检查Task信息
	 - Task<T>:能够返回Task类型及额外一个返回参数

使用三种返回类型时的调用方法格式为:
Void:

		CountCharacters(1, "https://www.baidu.com/")
	Task:
		Task task1 = CountCharacters(1, "https://www.baidu.com/")
	Task<T>:
		Task<int> t1 = CountCharacters(1, "https://www.baidu.com/")

2. 异步声明方法

	- 异步声明方法必须带有Async关键字
	- 异步声明方法参数中不能带有OUT或REF类型
	- 一个异步方法中可以有多个await,但是不能没有
	- 异步方法中可以使用匿名方法或Lambda表达式
	- 在异步方法中只有Await修饰的语句才为异步任务,Await之前的语句为同步执行
	- 异步调用的方法参数必须与声明方法的参数及返回类型一致
	- 带有返回参数类型<T>的返回值可以通过Task.result来获得

3. 必要的Await表达式

- 系统在官方提供了很多返回Task的方法,所以在调用官方方法时可检查是否有异步方法。如下面示例所调用的DownloadStringTaskAsync方法;
- 尽管有很多的官方支持的方法,但在有时候仍需要创建自己的异步方法;

注意:
使用Async/Await 特性其实并没有创建新线程,异步里任务也是在主线程里完成的。

看下一个Async/Await示例:此方法异步执行从设定的网站上下载并返回字符

 class DownloadProgram
    {        
        public void DoRun()
        {                    	
            Task<int> t1 = CountCharacters(1, "https://www.baidu.com/");//异步调用字段,此处其返回类型为Task<int>,
            //输入参数为String
            Console.WriteLine("Chars in https://www.baidu.com: {0}", t1.Result);
        }
        private async Task<int> CountCharacters(int id, string uriString)//异步声明方法,其定义异步方法的输入参数和返回类型,
        //在声明方法中Async关键字是必要的
        {
            WebClient wc1 = new WebClient(); 
            Console.WriteLine("Starting call {0} : {1, 10:N0} ms", id, sw.Elapsed.TotalMilliseconds);
            string result = await wc1.DownloadStringTaskAsync(new Uri(uriString));//Await关键字声明需要异步执行的步骤,其中 
             //DownloadStringTaskAsync是官方提供的用于异步执行的语句,不可替换成其他的DowanLoadString语句
            Console.WriteLine(" Call {0} completed: {1, 10:N0} ms", id, sw.Elapsed.TotalMilliseconds);
            return result.Length;
        }   
    }

二、在WPF如何使用Async/Await特性?

1.先展示一个WPF示例

l界面如下,当按下按钮Start开始执行异步方法,在下载完成网页内容将信息更新到上面的文本框中:
在这里插入图片描述

新建一个WPF程序,并在XAML文件中插入下面语句,简单规划界面布局

	 <Grid>
        <TextBox Name="ProcessTbox1" HorizontalAlignment="Left" Height="23" Margin="0,53,0,0" TextWrapping="Wrap" Text="Null" VerticalAlignment="Top" Width="792"/>
        <Button Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="209,189,0,0" VerticalAlignment="Top" Width="100" Height="37" Click="StartButton_Click"/>
        <Button Name="CancleButton" Content="Cancle" HorizontalAlignment="Left" Margin="334,189,0,0" VerticalAlignment="Top" Width="102" Height="37" Click="CancleButton_Click"/>

    </Grid>

在XAML.CS文件中插入执行语句如下:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        static async Task<int> ProcessReport(string Text)//异步声明方法
        {
            WebClient Wc1 = new WebClient();
            string strWebString = await Wc1.DownloadStringTaskAsync(new Uri(Text));//调用官方异步方法DownLoadStringTaskAsync
            return strWebString.Length;

        }

        private async void StartButton_Click(object sender, RoutedEventArgs e)//注意在按钮的Click事件前也添加了Async关键字,
        //此为必须的否则在实际运行中系统会报警不能正常执行
        {
            WebClient Wc1 = new WebClient();
            int iWebStringLength = await Task.Run(() => ProcessReport("https://www.baidu.com/"));//利用Await声明异步任务,
            //注意调用方法,Task.RUN()
            ProcessTbox1.Text = "The length of the web is" + Convert.ToString(iWebStringLength);
        }

        private void CancleButton_Click(object sender, RoutedEventArgs e)
        {
            ;
        }
        
    }

注意:

  • 给按钮添加异步方法时有两个Awync/Await特性调用,这两个特性是必要的否则未为按钮添加特性会导致系统报警

三、如何创建并使用自己的异步方法?

看如下示例:

 class DownloadProgram
    {
         Stopwatch sw = new Stopwatch();
        public void DoRun()
        {
            sw.Start();
            Task<int> t1 = CountCharacters(1, "https://www.baidu.com/");//调用异步方法
            
            Console.WriteLine("Chars in https://www.baidu.com: {0}", t1.Result);
           
        }
        private async Task<int> CountCharacters(int id, string uriString)//声明异步方法
        {
            WebClient wc1 = new WebClient(); 
            Console.WriteLine("Starting call {0} : {1, 10:N0} ms", id, sw.Elapsed.TotalMilliseconds);
            string result = await wc1.DownloadStringTaskAsync(new Uri(uriString));//Await声明异步任务调用DownloadStringTaskAsync
            Console.WriteLine(" Call {0} completed: {1, 10:N0} ms", id, sw.Elapsed.TotalMilliseconds);
            return result.Length;
        }     
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            DownloadProgram D1 =new DownloadProgram();
            D1.DoRun();

        }
    }

四、如何取消异步任务?

取消异步任务需要两个类:CancellationToken和CancellationTokenSource

当需要取消单个任务时,可以使用CancellationToken,如果需要取消多个异步任务时需要使用CancellationTokenSource
CancellationToken和CancellationTokenSource只包含了需要取消的信息,真正的取消需要在异步任务中去对输入参数做判断并停止任务
检查异步任务是否取消,需要在主任务中对CancellationToken或CancellationTokenSource进行判断,如果在异步任务中去做判断不能得到想要的结果

如下示例中,分别在三个地方对异步任务是否取消进行检查并用Console.WriteLine进行输出,可以预测下在哪里可以判断出异步任务已经取消:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp_CancleAsync
{
    class Program
    {
       
        static void Main(string[] args)
        {
            CancellationTokenSource Cts = new CancellationTokenSource();
            CancellationToken Ct1 = Cts.Token;
            Console.WriteLine("The calculation is begin");
            Task T1 = AsyncAddSequence(Ct1);
            Thread.Sleep(5000);
            Cts.Cancel();
            //Thread.Sleep(5000);//请注意当前线程主任务没有等待,如是加上此语句会有什么不同的结果?
            if (Ct1.IsCancellationRequested)//1、在主线程执行的任务中进行取消检查
            {
                Console.WriteLine("Main check: the AsyncTask is cancled");
            }

        }

        static async Task AsyncAddSequence(CancellationToken ct)
        {
            if (ct.IsCancellationRequested)//2、在异步方法中进行取消检查
            {
                Console.WriteLine("The task have been cancled1");
                return;
            }
            else
            {
                await Task.Run(() => CycleAdd(ct));
            }
           
        }
        static void  CycleAdd(CancellationToken ct)
        {
            int i1 = 0;
            for (int i = 1; i <= 100; i++)
            {
                if (ct.IsCancellationRequested)\\3、在异步任务方法中进行检查
                {
                    Console.WriteLine("The task have been cancled2");
                    Thread.Sleep(500);
                    return;
                }
                i1 += i;
                Thread.Sleep(500);
                Console.WriteLine("The sum of int is {0}", i1);
            }
        }
    }
}

最终输出为:

The calculation is begin
The sum of int is 1
The sum of int is 3
The sum of int is 6
The sum of int is 10
The sum of int is 15
The sum of int is 21
The sum of int is 28
The sum of int is 36
The sum of int is 45
Main check: the AsyncTask is cancled

在测试中发现:如果在主任务完成的时候,异步任务仍没有完成,则异步任务也取消执行,如果在上面示例中在Cts.Cancel()后再添加一个阻塞则在输出结果中就会出现异步任务中的WriteLine输出

Cts.Cancel();
Thread.Sleep(5000);//请注意当前线程主任务没有等待,如是加上此语句会有什么不同的结果?

添加后的输出为:

The calculation is begin
The sum of int is 1
The sum of int is 3
The sum of int is 6
The sum of int is 10
The sum of int is 15
The sum of int is 21
The sum of int is 28
The sum of int is 36
The sum of int is 45
The sum of int is 55
The task have been cancled2
Main check: the AsyncTask is cancled

另外,如果在异步任务执行之前执行了Cts.Cancel();,则会直接显示出异步方法里的输出语句:

The calculation is begin
The task have been cancled1
Main check: the AsyncTask is cancled

五、如何同步等待异步任务完成?

单个的异步任务可以调用Task.wait()来等待异步任务完成
如果需要多个异步任务完成则需要将多个异步任务合成数组,再调用Task数组的Waitall()或waitany()

将上面示例进行改装如下:

 class Program
    {
       
        static void Main(string[] args)
        {
            CancellationTokenSource Cts = new CancellationTokenSource();
            CancellationToken Ct1 = Cts.Token;
            Console.WriteLine("The calculation is begin");
           
            Task T1 = AsyncAddSequence(Ct1);
            //Cts.Cancel();
            T1.Wait();//等待异步任务完成
            //Console.WriteLine("Main check: the AsyncTask is completed");
            //Thread.Sleep(5000);
            
            //Thread.Sleep(5000);
            if (Ct1.IsCancellationRequested)
            {
                Console.WriteLine("Main check: the AsyncTask is cancled");
            }
            else
            {
                Console.WriteLine("Main check: the AsyncTask is Completed");
            }
            
        }

        static async Task AsyncAddSequence(CancellationToken ct)
        {
            if (ct.IsCancellationRequested)
            {
                Console.WriteLine("The task have been cancled1");
                return;
            }
            else
            {
                await Task.Run(() => CycleAdd(ct));
            }
           
        }
        static void  CycleAdd(CancellationToken ct)
        {
            int i1 = 0;
            for (int i = 1; i <= 20; i++)
            {
                if (ct.IsCancellationRequested)
                {
                    Console.WriteLine("The task have been cancled2");
                    Thread.Sleep(500);
                    return;
                }
                i1 += i;
                Thread.Sleep(500);
                Console.WriteLine("The sum of int is {0}", i1);
            }
        }
    }

输出结果如下:

The calculation is begin
The sum of int is 1
The sum of int is 3
The sum of int is 6
The sum of int is 10
The sum of int is 15
The sum of int is 21
The sum of int is 28
The sum of int is 36
The sum of int is 45
The sum of int is 55
The sum of int is 66
The sum of int is 78
The sum of int is 91
The sum of int is 105
The sum of int is 120
The sum of int is 136
The sum of int is 153
The sum of int is 171
The sum of int is 190
The sum of int is 210
Main check: the AsyncTask is Completed

六、如何异步的等待任务完成

异步等待任务与同步等待的区别在于异步等待不会占用主线程的时间
异步等待方法为task.WhenAll()和task.whenAny()
使用异步方法需要创建异步任务数组

 class Program
    {
       
        static void Main(string[] args)
        {
            CancellationTokenSource Cts = new CancellationTokenSource();
            CancellationToken Ct1 = Cts.Token;
            Console.WriteLine("The calculation is begin");
           
            Task T1 = AsyncAddSequence(Ct1);
            Task T2 = AsyncAddSequence(Ct1);

            Task[] Tasks =new Task[] { T1, T2 };//创建异步任务数组

            Task.WhenAll(Tasks);//异步等待任务完成
            
            
            Thread.Sleep(5000);//主线程需要阻塞,否则异步任务不能执行
            if (Ct1.IsCancellationRequested)
            {
                Console.WriteLine("Main check: the AsyncTask is cancled");
            }
            else
            {
                Console.WriteLine("Main check: the AsyncTask is Completed");
            }
            
        }

        static async Task AsyncAddSequence(CancellationToken ct)
        {
            if (ct.IsCancellationRequested)
            {
                Console.WriteLine("The task have been cancled1");
                return;
            }
            else
            {
                await Task.Run(() => CycleAdd(ct));
            }
           
        }
        static void  CycleAdd(CancellationToken ct)
        {
            int i1 = 0;
            for (int i = 1; i <= 20; i++)
            {
                if (ct.IsCancellationRequested)
                {
                    Console.WriteLine("The task have been cancled2");
                    Thread.Sleep(500);
                    return;
                }
                i1 += i;
                Thread.Sleep(500);
                Console.WriteLine("The sum of int is {0}", i1);
            }
        }
    }

输出如下:

The calculation is begin
The sum of int is 1
The sum of int is 1
The sum of int is 3
The sum of int is 3
The sum of int is 6
The sum of int is 6
The sum of int is 10
The sum of int is 10
The sum of int is 15
The sum of int is 15
The sum of int is 21
The sum of int is 21
The sum of int is 28
The sum of int is 28
The sum of int is 36
The sum of int is 36
The sum of int is 45
The sum of int is 45
Main check: the AsyncTask is Completed

七、如何暂停异步任务而不影响主线程任务的执行?

class Program
    {
       
        static void Main(string[] args)
        {
            CancellationTokenSource Cts = new CancellationTokenSource();
            CancellationToken Ct1 = Cts.Token;
            Console.WriteLine("The calculation is begin");
           
            Task T1 = AsyncAddSequence(Ct1);
            Task T2 = AsyncAddSequence(Ct1);

            Task[] Tasks =new Task[] { T1, T2 };

            Task.WhenAll(Tasks);
            //Cts.Cancel();
            //T1.Wait();
            //Console.WriteLine("Main check: the AsyncTask is completed");
            //Thread.Sleep(5000);
            
            Thread.Sleep(5000);
            if (Ct1.IsCancellationRequested)
            {
                Console.WriteLine("Main check: the AsyncTask is cancled");
            }
            else
            {
                Console.WriteLine("Main check: the AsyncTask is Completed");
            }
            
        }

        static async Task AsyncAddSequence(CancellationToken ct)
        {
            await Task.Delay(2000);//异步Task 延迟2S时间
            if (ct.IsCancellationRequested)
            {
                Console.WriteLine("The task have been cancled1");
                return;
            }
            else
            {
                await Task.Run(() => CycleAdd(ct));
            }
           
        }
        static void  CycleAdd(CancellationToken ct)
        {
            int i1 = 0;
            for (int i = 1; i <= 20; i++)
            {
                if (ct.IsCancellationRequested)
                {
                    Console.WriteLine("The task have been cancled2");
                    Thread.Sleep(500);
                    return;
                }
                i1 += i;
                Thread.Sleep(500);
                Console.WriteLine("The sum of int is {0}", i1);
            }
        }
    }

输出如下:
刚才开始异步任务会有两秒的停顿,然后再运行

The calculation is begin
The sum of int is 1
The sum of int is 1
The sum of int is 3
The sum of int is 3
The sum of int is 6
The sum of int is 6
The sum of int is 10
The sum of int is 10
The sum of int is 15
The sum of int is 15
Main check: the AsyncTask is Completed
  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值