我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C#使用Task实现异步方法

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C#使用Task实现异步方法

一、async和await特性的结构

1. 异步和同步

同步方法:如果一个方法被调用了,等待其执行所有处理后调用方法才继续执行的方法。

异步方法:异步方法能在处理完成之前就回到调用方法。

2.async和await

调用方法:该方法调用异步方法,然后在异步方法(可能在相同的线程,也可能在不同的线程)执行其任务时继续执行。

异步(async)方法:该方法异步执行其工作,被调用时立即回到调用方法。

await表达式:用于异步方法内部,指明需要异步执行的任务。一个异步方法需要至少包含一个await表达式。

二、什么是异步方法

1.异步方法的结构

异步方法在完成其工作之前即返回到调用方法,然后在调用方法继续执行的时候完成其工作。

  • 方法头包含async方法修饰符,该修饰符仅标识此方法是一个异步方法。
  • 包含至少一个await表达式,表示可以异步完成的任务。
  • 必须具备以下三种返回类型中的一种:void、Task、Task<T>。
  • 异步方法的参数可以是任意类型,但不能为ref或out。
  • 按照约定,异步方法的名称应该为Async为后缀。
  • 除了方法以外,Lambda表达式和匿名方法也可以作为异步对象。

2.异步方法三种返回类型理解

1. Task<T> :如果调用方法要从调用中获取一个T类型的值,异步方法的返回类型就必须是Task<T>。调用方法通过读取Task的Result属性来获取这个T类型的值。

    // 使用返回Task<int>对象的异步方法代码示例:    
    static class DoAsyncStuff 
    {
        public static async Task<int> CalculateSumAsync(int i1, int i2) 
        {
            int sum = await Task.Run(() => GetSum(i1, i2));
            return sum;//注意返回的是一个int类型
        }
 
        private static int GetSum(int i1, int i2) { return i1 + i2; }
    
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Task<int> task = DoAsyncStuff.CalculateSumAsync(1, 2);
            //处理其他事情...
            Console.WriteLine("Value:{0}",task.Result);//调用方法通过Result获取这个int类型的值
 
        }
    }

2.Task : 如果调用方法不需要从异步方法中返回某个值,但需要检查异步方法的状态,那么异步方法可以返回一个Task类型的对象,这时即使异步方法中出现了return语句,也不会返回任何东西。

    //使用Task不返回类型的异步方法代码示例:
    static class DoAsyncStuff
    {
        public static async Task CalculateSumAsync(int i1, int i2)
        {
            int sum = await Task.Run(() => GetSum(i1, i2));
            Console.WriteLine("Value:{0}",sum);
        }
 
        private static int GetSum(int i1, int i2) { return i1 + i2; }
 
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Task task = DoAsyncStuff.CalculateSumAsync(1, 2);
            //处理其他事情...
            task.Wait();
            Console.WriteLine("异步方法结束");
        }
    }

3. void :如果调用方法仅仅想执行异步方法,而不需要与它进行进一步交互时【称为“调用并忘记”】,异步方法可以返回void类型。

    //使用“调用并忘记”的异步方法的代码示例:
    static class DoAsyncStuff
    {
        public static async void CalculateSumAsync(int i1, int i2)
        {
            int sum = await Task.Run(() => GetSum(i1, i2));
            Console.WriteLine("Value:{0}",sum);
        }
 
        private static int GetSum(int i1, int i2) { return i1 + i2; }
 
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            DoAsyncStuff.CalculateSumAsync(1, 2);
            //处理其他事情...
            Thread.Sleep(200);
            Console.WriteLine("Program Exiting");
        }
    }

3.异步方法的控制流

1. 异步方法的结构包含三个不同的区域:

 控制流阐述:

  • 调用异步方法后,知道遇到第一个await表达式之前都是同步的。
  • 遇到await表达式之后,异步方法将控制返回到调用方法。如果方法的返回类型为Task或Task<T>类型,将创建一个Task对象,表示需异步完成的任务和后续,然后将Task返回到调用方法。
  • 到目前为止,已经出现两个控制流,异步方法内的和调用方法内的。异步方法的代码完成以下工作:
    • 异步执行await表达式的空闲任务。
    • 当await表达式完成时,执行后续部分。如果后续还有await表达式,将重复此步骤:异步执行await表达式,然后执行后续部分。
  • 当后续部分遇到return语句或到达方法末尾时:
    • 如果方法返回类型为void,控制流将退出。
    • 如果方法返回类型为Task,后续部分设置Task的属性并退出。
    • 如果方法返回类型为Task<T>,后续部分还将设置Task的Result属性。
  • 同时调用方法中的代码继续其进程,从异步方法获取Task对象。当需要其实际值时,就引用Task对象的Result属性,届时,如果异步方法设置了该属性,调用方法就能调用其值并继续,否则将暂停等待该属性被设置,然后再继续执行。

 需要注意的是:异步方法的return语句并没有真正返回一个值,它只是退出了,异步方法中的返回类型始终是方法头中声明的返回类型。

三、await表达式

await表达式指定了一个异步执行的任务。这个任务可能是一个Task类型的对象,也可能不是,默认情况下这个任务在当前线程异步运行。

我们可能需要编写自己的方法作为await表达式的任务。最简单的方式是在你的方法中使用Task.Run创建一个Task。(关于Task.Run即在不同的线程上运行你的方法)Task.Run方法有8个重载 ,如下图所示:

以Func<int> 委托作为参数的代码示例:

    class MyClass
    {
        public int Get10() 
        {
            return 10;
        }
 
        public async Task DoWorkAsync() 
        {
            //方式1:使用Get10创建名为ten的Func<int>委托,将该委托传入Task.Run方法
            Func<int> ten = new Func<int>(Get10);
            int a = await Task.Run(ten);
 
            //方式2:直接在Task.Run中创建委托,传入Get10方法
            int b = await Task.Run(new Func<int>(Get10));
 
            //方式3:使用与Func<T>兼容的Lambda表达式,Lambda表达式将隐式转换为该委托
            int c = await Task.Run(() => { return 10; });
 
            Console.WriteLine("{0},{1},{2}",a,b,c);
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Task task = new MyClass().DoWorkAsync();
            task.Wait();
        }
    }

从Task.Run的重载中可以发现,可以作为Run中第一个参数的委托有四种:

使用四种委托作为参数的Run方法代码示例:

    class MyClass
    {
        public static async Task DoWorkAsync() 
        {
            await Task.Run(() => Console.WriteLine(5.ToString()));//Aciton,无参无返
 
            Console.WriteLine((await Task.Run(() => 6)).ToString());//TResult Func(),无参有返,返回TResult类型对象
 
            await Task.Run(() => Task.Run(() => Console.WriteLine(7.ToString())));//Task Func(),无参有返,返回简单Task对象
 
            Console.WriteLine((await Task.Run(() => Task.Run(() => 8))).ToString());//Task<TResult> Func(),无参有返,返回Task<T>对象
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Task task = MyClass.DoWorkAsync();
            task.Wait();
        }
    }

假如我们需要一个接受参数的方法,但是无法匹配上述的四种委托(接受没有参数的方法的委托),我们可以创建一个Lambda表达式,其唯一行为就是运行我们的接受参数的方法,来满足Run方法所能接受的委托形式。代码示例如下:

    class MyClass
    {
        //需要使用异步的方法
        public static int GetSum(int a, int b) { return a + b; }
 
        public static async Task DoWorkAsync(int a,int b) 
        {
            int result = await Task.Run(() => GetSum(a, b));//创建一个满足Func<TResult>委托形式的Lambda表达式
            Console.WriteLine(result);
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Task task = MyClass.DoWorkAsync(6,7);
            task.Wait();
        }
    }

四、取消一个异步操作

一些.NET异步方法允许你请求终止执行,我们可以在自己的异步方法中加入这个特性。有两个类:CancellationToken和CancellationTokenSource是专门为了取消异步操作设计的。

1. CancellationToken对象包含了一个任务是否应被取消的消息。拥有CancellationToken对象的任务需要定期检查其令牌(token)的状态,如果CancellationToken对象中的IsCancellationRequested属性为true,任务需停止其操作并返回。CancellationToken是不可逆的,也就是一旦IsCancellationRequested设置为true就不能更改了。

2.  CancellationTokenSource对象创建可分配给不同任务的CancellationToken对象。任何持有CancellationTokenSource的对象都可以调用其Cancel方法,这会将CancellationToken对象中的IsCancellationRequested设置为true。

3. 使用这两个取消类的代码:

    class MyClass
    {
        public async Task RunAsync(CancellationToken ct) 
        {
            if (ct.IsCancellationRequested)
                return;
            await Task.Run(()=>CycleMethod(ct));
        }
 
        void CycleMethod(CancellationToken ct) 
        {
            Console.WriteLine("开始CycleMethod方法");
 
            const int max = 5;
 
            for (int i = 0; i < max; i++) 
            {
                if (ct.IsCancellationRequested)
                    return;
                Thread.Sleep(1000);
                Console.WriteLine("完成:{0}/{1}",i+1,max);
            }
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            CancellationToken ct = cts.Token;
 
            MyClass mc = new MyClass();
 
            Task t = mc.RunAsync(ct);
 
            
            //Thread.Sleep(3000);
            //cts.Cancel();//解除注释将产生取消异步的操作
 
            t.Wait();
            Console.WriteLine("是否取消了异步方法:{0}", ct.IsCancellationRequested);
 
        }
    }

五、异常处理的await表达式

就使用try...catch...正常处理异常就行。

    class MyClass
    {
        public static async Task BadAsync() 
        {
            try
            {
                await Task.Run(() => { throw new Exception(); });
            }catch (Exception ex) 
            {
                Console.WriteLine("异步中抛出异常...");
            }
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Task task = MyClass.BadAsync();
            task.Wait();
            Console.WriteLine("Task Status:{0}",task.Status);//Task Status:RanToCompletion,因为Task没有被取消
            Console.WriteLine("Task IsFaulted:{0}",task.IsFaulted);//Task IsFaulted:False,因为没有未处理的异常
 
 
        }
    }

六、在调用方法中同步地等待任务

1. Wait

Task的Wait方法可以让调用方法等待异步方法完成,在Wait方法处,流程是阻塞的。

    class MyClass
    {
        public void PrintA()
        {
            const int max = 10;
            for (int i = 0; i < max; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine("A任务完成:{0}/{1}", i + 1, max);
            }
        }
        public async Task ForWaitAsync() 
        {
            await Task.Run(new Action(PrintA));
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            MyClass mc = new MyClass();
            Task t = mc.ForWaitAsync();
 
            Thread.Sleep(6000);//主线程休眠6秒
 
            //等待任务完成,使用此方法可以让主方法等待异步方法完成,否则主方法休眠六秒结束时异步方法也将直接结束
            t.Wait();
        }
    }

2. WaitAll和WaitAny

Wait是等待单一任务完成,我们也可以使用WaitAll和WaitAny等待多个任务。(这两个方法是同步方法,知道等待条件满足后再继续执行,不然流程会阻塞在那里)

    class MyClass
    {
        public void PrintA()
        {
            const int max = 10;
            for (int i = 0; i < max; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine("A任务完成:{0}/{1}", i + 1, max);
            }
            Console.WriteLine("A任务全部完成!");
        }
 
        public void PrintB()
        {
            const int max = 5;
            for (int i = 0; i < max; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine("B任务完成:{0}/{1}", i + 1, max);
            }
            Console.WriteLine("B任务全部完成!");
        }
 
        public async Task AForWaitAsync() 
        {
            await Task.Run(new Action(PrintA));
        }
 
        public async Task BForWaitAsync()
        {
            await Task.Run(new Action(PrintB));
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            MyClass mc = new MyClass();
            Task a = mc.AForWaitAsync();
            Task b = mc.BForWaitAsync();
 
            Task[] task = new Task[] { a, b };//存放Task的数组
 
            Thread.Sleep(8000);//调用方法休眠8秒
 
            //Task.WaitAll(task);//等待所有任务完成
            Task.WaitAny(task);//等待任意一个任务完成后将不再阻塞
 
        }
    }

WaitAll和WaitAny分别还包含四个重载:

七、在异步方法中异步地等待任务 

有时在异步方法中,你会希望用await表达式来等待Task。这时异步方法会返回到调用方法,但该异步方法会等待一个或所有任务完成。可以通过Task.WhenAll或Task.WhenAny方法来实现。这两个方法称为组合子。

    class MyClass
    {
        public void PrintA()
        {
            const int max = 10;
            for (int i = 0; i < max; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine("A任务完成:{0}/{1}", i + 1, max);
            }
        }
 
        public void PrintB()
        {
            const int max = 5;
            for (int i = 0; i < max; i++)
            {
                Thread.Sleep(1000);
                Console.WriteLine("B任务完成:{0}/{1}", i + 1, max);
            }
        }
 
        public async Task AAsync() 
        {
            await Task.Run(new Action(PrintA));
        }
 
        public async Task BAsync()
        {
            await Task.Run(new Action(PrintB));
        }
 
        public async Task ForWhenAsync()
        {
            Task a = AAsync();
            Task b = BAsync();
 
            Task[] tasks = { a, b };//Task数组
 
            //await Task.WhenAll(tasks);//在异步中等待所有任务
            await Task.WhenAny(tasks);//在异步中等待任意一个任务
 
            //此异步方法中的流程会阻塞在Task.WhenAny(tasks)中,直到某个条件满足才会到达这里
            Console.WriteLine("现在任务A是否完成:{0}",a.IsCompleted?"Yes":"No");
            Console.WriteLine("现在任务B是否完成:{0}",b.IsCompleted?"Yes":"No");
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            const int max = 12;
 
            MyClass mc = new MyClass();
            mc.ForWhenAsync();
 
            for (int i = 0;i < max ; i++) 
            {
                Thread.Sleep(1000);
                Console.WriteLine("Main任务完成:{0}/{1}", i + 1, max);
            }
 
        }
    }

八、Task.Delay方法

Task.Delay方法创建一个对象,该对象将暂停其在线程中的处理,并在一定时间之后完成。和Thread.Sleep阻塞线程不同的是,Task.Delay方法不会阻塞线程,线程可以继续处理其他工作。

    class Simple
    {
        Stopwatch sw = new Stopwatch();
 
        public void DoRun() 
        {
            Console.WriteLine("调用之前");
            ShowDelayAsync();
            Console.WriteLine("调用之后");
        }
 
        private async void ShowDelayAsync() 
        {
            sw.Start();
            Console.WriteLine("Delay 执行之前:{0}",sw.ElapsedMilliseconds);
            await Task.Delay(1000);//创建了一个Task对象,该对象将暂停其在线程中的处理,所以在打印下一句话之前,控制回到了调用方法打印调用方法中的"调用之后"
            Console.WriteLine("Delay 执行之后:{0}", sw.ElapsedMilliseconds);
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Simple simple = new Simple();
            simple.DoRun();
            Console.Read();
            
        }
    }

到此这篇关于C#使用Task实现异步方法的文章就介绍到这了,更多相关C# Task异步内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C#使用Task实现异步方法

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C#使用Task实现异步方法

本文主要介绍了C#使用Task实现异步方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-05-14

C#中异步方法返回的void和Task有什么区别

C#中异步方法返回的void和Task有什么区别?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。C#异步方法返回void和Task的区别如果异步(async关键字
2023-06-06

使用Java实现异步编程的方法

本篇文章给大家分享的是有关使用Java实现异步编程的方法,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。什么是异步?为什么要用它?异步编程提供了一个非阻塞的,事件驱动的编程模型。
2023-05-31

C++异步数据交换实现方法介绍

这篇文章主要介绍了C++异步数据交换实现方法,异步数据交换,除了阻塞函数send()和recv()之外,Boost.MPI还支持与成员函数isend()和irecv()的异步数据交换
2022-11-21

C#实现异步的常用方式总结

这篇文章主要为大家详细介绍了C#实现异步的几个常用方式,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以了解一下
2023-05-19

javascript实现异步的方法

这篇文章给大家分享的是有关javascript实现异步的方法的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。javascript实现异步的方法:1、使用setTimeout方法;2、使用setImmediate 方
2023-06-14

C#BeginInvoke实现异步编程方式

这篇文章主要介绍了C#BeginInvoke实现异步编程方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-01-28

C#中如何实现异步调用

C#中如何实现异步调用,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。.NET Framework 允许您C#异步调用任何方法。定义与您需要调用的方法具有相同签名
2023-06-17

C#中怎么实现异步调用

C#中怎么实现异步调用,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。首先,C#异步调用出来的新线程,必须回收,不回收是浪费资源的可耻行为,.NET也是不允许的,所以你别想钻
2023-06-17

C#怎么使用async和await实现异步编程

这篇文章主要介绍“C#怎么使用async和await实现异步编程”,在日常操作中,相信很多人在C#怎么使用async和await实现异步编程问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#怎么使用async
2023-07-02

JS异步遍历的实现方法

本文主要介绍了JS异步遍历的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-05-17

java 如何快速实现异步调用方法

java 如何快速实现异步调用方法 什么是异步编程CompletableFuturejava 演示 什么是异步编程 在实现异步调用之前,我们先了解一下,什么是异步编程?什么场景下适用等等情况。 我们都知道,在传统的同步编程中
2023-08-17

C#怎么使用Task实现执行并行任务

这篇“C#怎么使用Task实现执行并行任务”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“C#怎么使用Task实现执行并行任务
2023-07-05

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录