• .net的 async 和 await


    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()之前,主线程已经执行完了

  • 相关阅读:
    char
    export和export default
    递归打印文件目录
    centso7 安装redmine
    sonar结合jenkins
    sonar安装
    gitlab+jenkins
    centos5 安装redmine
    elk安装最佳实践
    elk认证模块x-pack安装
  • 原文地址:https://www.cnblogs.com/niuzaihenmang/p/5633314.html
Copyright © 2020-2023  润新知