.net的async和await

那就让我这样吧 那就让我这样吧     2022-07-30     596

关键词:

async 和 await 出现在C# 5.0之后,关系是两兄弟,Task是父辈,Thread是爷爷辈,这就是.net 多线程处理的东西,具体包括 创建线程,线程结果返回,线程中止,线程中的异常处理

1 线程创建的几个方式

 static void Main(string[] args)
        {
            new Thread(NewThread).Start();//这里需要注意:创建一个 new Thread()的实例的时候,需要手动调用它的Start()去启动这个实例,
            //对于Task来说StartNew和Run的同时,既会创建新的线程,又会自动启动这个线程
            Task.Factory.StartNew(NewThread);
            Task.Run(new Action(NewThread));
        }
        public static void NewThread()
        {
            Console.WriteLine("我是一个New线程!");
        }
View Code

2 使用线程池

        //线程的创建是比较耗费资源的一件事情,.net提供了线程池来帮助创建和管理线程,Task默认会直接使用线程池
        //但是Thread不会,如果不使用Task,又想使用线程池,可以使用ThreadPool类

        static void Main(string[] args)
        {
            Console.WriteLine("我是主线程:ThreadId为{0}", Thread.CurrentThread.ManagedThreadId);
            ThreadPool.QueueUserWorkItem(NewThread);
            Console.ReadKey();
        }
        public static void NewThread(object data)
        {
            Console.WriteLine("我是一个New线程,线程Id是{0}", Thread.CurrentThread.ManagedThreadId);
            Console.ReadKey();
        }
View Code

3 传入参数

 //传入参数
        static void Main(string[] args)
        {
            new Thread(NewThread).Start("arg1");//没有匿名委托之前只能这样传入一个参数
            //有了匿名委托之后,可以传入多个参数
            new Thread(delegate()
                {
                    NewThread2("arg1", "arg2", "arg3");
                }).Start();

            //Lambda匿名委托
            new Thread(() =>
            {
                NewThread2("arg1", "arg2", "arg3");
            }).Start();
        }
        public static void NewThread()
        {
            Console.WriteLine("我是一个New线程!");
        }
        public static void NewThread2(string arg1, string arg2, string arg3)
        {
            Console.WriteLine("我是一个New线程!");
        }
View Code

4 返回值

        //返回值
        //Thread这玩意是没有返回值的,但是高级的Task可以
        static void Main(string[] args)
        {
            var str = Task.Run<string>(() =>
             {
                 return DateTime.Now.ToString();
             });
        }
View Code

5 线程之间数据共享

        //线程之间共享数据问题(这种存在问题:如果第一个线程还没来得及把_isOK设置为True,第二个线程就进来了,这样在多线程情况下,结果不可预知,这就是线程不安全)
        private static bool _isOK = false;
        static void Main(string[] args)
        {
            new Thread(DoOk).Start();
            Task.Factory.StartNew(DoOk);
            Task.Run(() =>
            {
                DoOk();
            });

            Task.Run(new Action(DoOk));
        }

        static void DoOk()
        {
            if (!_isOK)
            {
                Console.WriteLine("OK");
                _isOK = true;
                Console.ReadKey();
            }
        }
View Code

6 独占锁

 //解决上面线程不安全的问题就要用到锁(锁的类型有 读写锁,独占锁,互斥锁)
        //独占锁
        private static bool _isOK = false;
        private static object _lock = new object();
        static void Main(string[] args)
        {
            new Thread(DoOk).Start();
            Task.Factory.StartNew(DoOk);
            Task.Run(() =>
            {
                DoOk();
            });

            Task.Run(new Action(DoOk));
        }

        static void DoOk()
        {
            lock (_lock)//独占锁,加上锁之后,被锁的代码在同一个时间内,只允许一个线性进行访问,
            //其他线程会被阻塞排队,只有这个线程被释放之后,其他线程才能执行被锁的代码,因为这时候,之前的线程已经访问完毕,锁已经被释放
            {
                if (!_isOK)
                {
                    Console.WriteLine("OK");
                    _isOK = true;
                    Console.ReadKey();
                }
            }
        }
View Code

7 线程量(信号量)

        //SemaphoreSlim 可以控制对某一段代码或者对某个资源访问的线程的数量,超过这个数量其他线程就得等待,等可以访问的数量的线程访问完之后,其他线程才可以继续访问,
        //跟锁的原理相似,但不是独占的,可以允许一定数量的线程同时访问

        static SemaphoreSlim _sem = new SemaphoreSlim(3);
        static void Main(string[] args)
        {
            for (int i = 1; i <= 5; i++)
            {
                //new Thread(() => 
                //{
                //    Entry(i);
                //}).Start();
                new Thread(Entry).Start(i);
            }
        }
        static void Entry(object id)
        {
            Console.WriteLine(id + "开始排队...");
            _sem.Wait();
            Console.WriteLine(id + "开始执行...");
            Thread.Sleep(1000 * (int)id);
            Console.WriteLine(id + "执行完毕,离开");
            _sem.Release();
            Console.ReadKey();
        }
View Code

8 捕获异常

a:

 //使用Thread 线程的异常处理(其他线程的异常,主线程能捕获到么?)
        static void Main(string[] args)
        {
            try
            {
                new Thread(Entry).Start();
            }
            catch (Exception ee)
            {
                //这里其他线程的异常是捕获不到的
                Console.WriteLine("捕获到异常!");
            }
            Console.ReadKey();
        }
        static void Entry()
        {
            throw null;
        }
View Code

 b:

    static void Main(string[] args)
        {
            try
            {
                var task = Task.Run(() =>
                   {
                       Entry();
                   });
                task.Wait();//调用这句话之后主线程才能捕获task里面的异常

                //对于有返回值的Task,接收了它的返回值就不需要再调用Wait(),Entry2()里面的异常有可以捕获到
                var task2 = Task.Run(() =>
                {
                    return Entry2();
                });
               var name = task2.Result;
            }
            catch (Exception)
            {

                Console.WriteLine("捕获到异常!");
                Console.ReadKey();
            }
        }
        static void Entry()
        {
            throw null;
        }
        static string Entry2()
        {
            throw null;
        }
View Code

 前面是铺垫,现在进入本文主题

        static void Main(string[] args)
        {
            Test2();//这个方法其实多余,本来可以直接写  await Test2(); 但是因为控制台入口方法不支持await
            Console.WriteLine("当前线程Id" + Thread.CurrentThread.ManagedThreadId);
            Console.ReadKey();
        }
        //方法上打上async关键字,就可以用await调用其他打上async的方法了
        //await后面的方法将在另外一个线程中执行
        //返回值前面加上 async,方法里面就可以用 await 了
        static async Task Test()
        {
            await Test2();
        }
        static async Task Test2()
        {
            //.net 4.5 才有Delay
            await Task.Delay(1000);
            Console.WriteLine("当前线程Id:" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("进入其他线程");
        }
View Code

 注意:await不会开启新的线程,当前线程一直往下走,直到遇到async方法,这个方法内部会调用 Task.Run或者Task.Factory.StartNew开启新的线程

也就是说方法如果不是 async,我们需要自己手动去创建Task,才会真正创建线程

 

  static void Main(string[] args)
        {
            Console.WriteLine("当前线程MainId" + Thread.CurrentThread.ManagedThreadId);
            Test();
            Console.ReadKey();
        }

        static async Task Test()
        {
            Console.WriteLine("当前线程TestId" + Thread.CurrentThread.ManagedThreadId);
            var name =  Test2();//这里没有用await,所以下面的代码可以继续执行
            Console.WriteLine("结束调用Test2," + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("获取Test2的返回值" + await name + "Id:" + Thread.CurrentThread.ManagedThreadId);

        }

        static async Task<string> Test2()
        {
            Console.WriteLine("当前线程Test2Id" + Thread.CurrentThread.ManagedThreadId);
            return await Task.Run(() => 
            {
                Thread.Sleep(1000);
                Console.WriteLine("当前线程Test22Id" + Thread.CurrentThread.ManagedThreadId);
                return "MyName";
            });
        }
View Code

由上可以看出:

await 并不是针对async,而是针对方法返回的Task,所以所有的 async方法都必须返回Task,所以同样可以在Task前面加await,这也是告诉编译器需要等这个Task的返回值或者等这个Task执行完毕之后才能继续走下去

 加上await关键字之后,后面的代码会被挂起等待,直到task执行完毕有返回值的时候才能继续向下执行,这一段时间主线程会处于挂起状态,

 static void Main(string[] args)
        {
            Console.WriteLine("当前线程MainId" + Thread.CurrentThread.ManagedThreadId);
            //Test();

            var task = Task.Run(() =>
            {
                Test3();
            });

            task.GetAwaiter().GetResult();
            Console.WriteLine("主线程");
            Console.ReadKey();
        }
        static void Test3()
        {
            Console.WriteLine("新开进程");
        }
View Code

Task.GetAwait()方法会给我们返回一个awaitable的对象,通过调用这个对象的GetResult方法就会挂起主线程

当然也有例外,比如在调用Task.GetAwait()之前,主线程已经执行完了

async和await之异步编程的学习

     async修改一个方法,表示其为异步方法。而await表示等待一个异步任务的执行。js方面,在es7中开始得以支持;而.net在c#5.0开始支持。本文章将分别简单介绍他们在js和.net中的基本用法。一、在js中的实现js... 查看详情

async和await之异步编程的学习

     async修改一个方法,表示其为异步方法。而await表示等待一个异步任务的执行。js方面,在es7中开始得以支持;而.net在c#5.0开始支持。本文章将分别简单介绍他们在js和.net中的基本用法。一、在js中的实现js... 查看详情

如何在 ASP.NET Web API 中使用非线程安全的 async/await API 和模式?

】如何在ASP.NETWebAPI中使用非线程安全的async/awaitAPI和模式?【英文标题】:Howtousenon-thread-safeasync/awaitAPIsandpatternswithASP.NETWebAPI?【发布时间】:2014-01-2609:32:24【问题描述】:这个问题是由EFDataContext-Async/Await&Multithreading触发的。... 查看详情

在 .net 4 上使用 async-await

】在.net4上使用async-await【英文标题】:Usingasync-awaiton.net4【发布时间】:2012-02-2423:56:55【问题描述】:我目前正在开始创建一个应用程序,该应用程序将从C#5的async-await功能中获益良多。但我不确定要使用哪个版本的VS和异步运... 查看详情

通过 ASP.NET Web API 有效地使用 async/await

】通过ASP.NETWebAPI有效地使用async/await【英文标题】:Effectivelyuseasync/awaitwithASP.NETWebAPI【发布时间】:2015-09-2000:17:55【问题描述】:我正在尝试在我的WebAPI项目中使用ASP.NET的async/await功能。我不太确定它是否会对我的WebAPI服务的性... 查看详情

使用async和await方法来

先看直接的代码请求方式地啊;这里是我们同步方法的实现:usingSystem;usingSystem.Collections.Generic;usingSystem.Diagnostics;usingSystem.Linq;usingSystem.Net;usingSystem.Text;usingSystem.Threading.Tasks;namespaceConsoleApplication 查看详情

多线程async&await

...取消等操作,可谓已经很强大了。但.net4.5为我们带来了async&await,使得实现多线程的写法更简单,更优美,更符合线性思维。下面通过一个例子来演示通过Task和async&await分别如何实现,并且最后还附上代码执行顺序图。使... 查看详情

[译]c#5.0中的async和await(整理中...)

C#5.0中的Async和Await【博主】反骨仔    【本文】http://www.cnblogs.com/liqingwen/p/6069062.html  伴随着.NET4.5和VisualStudio2012的C#5.0,我们可以使用的新的异步模式,这里涉及到async和await关键字。有许多不同点的观点,比起之前我们... 查看详情

如何正确理解.net4.5和c#5.0中的async/await异步编程模式

...术可能比较复杂,使它们难以编写、调试和维护。C#中的async和await关键字都是异步编程的核心。通过使用这两个关键字,可以使用.NETframework或Windows运行时中的资源轻松创建异步方法(几乎与创建同步方法一样轻松)。通过使用... 查看详情

正确使用 Task.Run 和 async-await 时

】正确使用Task.Run和async-await时【英文标题】:WhencorrectlyuseTask.Runandwhenjustasync-await【发布时间】:2013-08-0312:36:15【问题描述】:我想询问您对何时使用Task.Run的正确架构的看法。我在我们的WPF.NET4.5中遇到了滞后的UI应用程序(使... 查看详情

正确使用 Task.Run 和 async-await 时

】正确使用Task.Run和async-await时【英文标题】:WhencorrectlyuseTask.Runandwhenjustasync-await【发布时间】:2022-01-0702:41:54【问题描述】:我想询问您对何时使用Task.Run的正确架构的看法。我在我们的WPF.NET4.5中遇到了滞后的UI应用程序(使... 查看详情

async / await 如何在 ASP.Net 应用程序中提供帮助?

】async/await如何在ASP.Net应用程序中提供帮助?【英文标题】:Howasync/awaitcanhelpinASP.Netapplication?【发布时间】:2015-03-0711:14:42【问题描述】:在MVC控制器的action方法中使用async/await可以扩展Web应用程序,因为在await时,Asp.Net线程池... 查看详情

调试 .NET Async/Await 函数时无法检查变量

】调试.NETAsync/Await函数时无法检查变量【英文标题】:Can\'tInspectVariablesWhenDebugging.NETAsync/AwaitFunctions【发布时间】:2013-02-1312:04:39【问题描述】:我有一个使用async/await功能的.NET4.5项目。当我尝试在等待语句之后检查/快速观察变... 查看详情

async/await的串行和并行

参考技术A2.Promise then来实现3.通过async和await来实现1.只有在async修饰的方法中才可以使用await2.async修饰的方法会自动返回一个Promise1.await关键字还有可以在async修饰的方法中使用2.await后面必须要跟一个Promise3.await会将异步函数... 查看详情

如何在.net4.0中使用.net4.5的async/await实现异步

方法一:使用Microsoft.Bcl.Async看好说明,在XP下需要打补丁,优点是补了很多扩展的返回Task的异步方法方法二:使用AsyncBridge,有点事轻巧,不需要打补丁你可以结合他们的源码,基于AsyncBridge源码再根据Microsoft.Bcl.Async的源代码自... 查看详情

async和await(代码片段)

async和awaitasync的使用await的使用在async和await出来之前,异步回调的方法就有1、回调嵌套2、Promsie的链式回调3、Generator的复杂繁琐调用方式async/await采用同步的思维来解决异步问题的方式,使代码的可读性更强了。async的使... 查看详情

async和await总结

...想要promise,想要promise异步执行的成功的value数据3.哪里写async?await所在函数(最近的)定义的左侧写async 查看详情

使用 .NET Core(.NET Standard 1.4 和 .NET Framework 4.6.1)对 System.Net.Http 使用 await/async 时出现错误?

...ore(.NETStandard1.4和.NETFramework4.6.1)对System.Net.Http使用await/async时出现错误?【英文标题】:Bugwhenusingawait/asyncforSystem.Net.Httpwith.NETCore(.NETStandard1.4and.NETFramework4.6.1)?【发布时间】:2017-01-1715:23:08【问题描述】:前景:我正在调 查看详情