首页 > 其他分享 >01-02异步多线程基础概念

01-02异步多线程基础概念

时间:2023-11-03 18:57:08浏览次数:37  
标签:02 01 Console Thread Task task 线程 WriteLine 多线程

  • 任何异步多线程都离不开委托delegate --lambda-action/Func
  • 委托的异步调用
  • 异步多线程:发起调用,不等待结束就直接进入下一行(主线程)
    • 动作会有新的线程执行.

线程特点

  • 特点一:

    • 多线程和界面使用(说白了,多线程就是为了给大量计算创建子线程,然后先执行完后面代码)
      • 场景一: 一个计算量子语言的按钮, 测量光子速度的按钮,这两个按钮之间并没有太大的联系(不卡UI界面,多线程妙用)
        • 同步单线程: 小明点击了量子语言按钮陷入了大量的计算, 此时点击光子按钮没反应,这是因为单线程量子语言的计算没有完成,只有完成,你才能重新开启一个线程.
        • 异步多线程: 点击量子语言,创建了量子语言的子线程后端计算,代码照常执行完成,此时界面因为没有卡代码所以,你可以继续点击,
  • 特点二:

    • 多线程并不会随着你写的线程数而增加
      • cup: 1核3线程
      • 1个线程时间13000ms ,5个线程 4269 ,性能只有三倍
      • 多个线程之间的协调需要资源, 例如线程的创建管理都是资源
      • 物理上只有3线程,你写了5线程并没有更快.
  • 特点三:

  • 根据第一张图片,你看异步中,start线程顺序和end线程顺序他不是一张

  • 图片中我们启动时,是0,1,2,3,4,顺序,但是到打印这个线程编号时过了一段时间,所以线程的启动不是有顺序的.

  • 同一个线程执行的时间都不一致,这个能力跟cpu的调度有关,所以执行看运气.

  • 特点四:

    • 线程他是不可预测的,所以对于有些需要顺序执行的线程就很烦恼,避免下面的坑:
      • 不要尝试使用延时一个线程,然后再一个线程,巨大的坑不要尝试
      • 不要使用多模式的风骚操作

多线程.net的版本介绍

// .NerFramework 1.0 1.1
            // 首先Thread线程api非常丰富,可以玩的非常花俏,但是线程是系统分发的,其实这些操作不太好
            // Thread启动线程是没有控制的, 所以你可以启动一万个,但是死机,所以不安全.
            ThreadStart _threadStart = () =>
            {
                Console.WriteLine("**********************异步方法开始了**********************");
                Thread.Sleep(2000);
                Console.WriteLine($"**********************异步方法结束了{Thread.CurrentThread.ManagedThreadId}**********************");
            };
            Thread _thread = new Thread(_threadStart);
            _thread.Start();

        }    
 Console.WriteLine("");
            // .NerFramework 2.0
            // ThreadPool :所以TreadPool池化资源设计思想,线程是一种资源,之前我们1.0时代,你要用就去找计算机申请,而池化是一个容器,现在你要线程就去找这个容器去拿
            // ThreadPool 限制了最大线程数量 ,Api又太少了,控制顺序能力太弱了,不好用
            WaitCallback _waitCallback = o =>
            {
                Console.WriteLine("**********************异步方法开始了**********************");
                Thread.Sleep(2000);
                Console.WriteLine($"**********************异步方法结束了{Thread.CurrentThread.ManagedThreadId}**********************");
            };
            ThreadPool.QueueUserWorkItem(_waitCallback);

  1. 这个也是我们目前使用的方法
 // .NerFramework 3.0
            // Task 多线程最好使用方式,提供了丰富api,也有线程池,非常适合开发.

            Action _action = () =>
            {
                Console.WriteLine("**********************异步方法Task开始了**********************");
                Thread.Sleep(2000);
                Console.WriteLine($"**********************异步方法Task结束了{Thread.CurrentThread.ManagedThreadId}**********************");
            };
            Console.WriteLine("");

            Task _task = new Task(_action);
            _task.Start();
  1. 额外的并发主线程和子线程
    Console.WriteLine("");
            // Parallel 他可以让主线程和多线程并发运行,不浪费主线程的运算资源,其他的是直接让子线程完成,这个是主线程也当做子线程运算.
            // 
            WaitCallback _waitCallback = o =>
            {
                Console.WriteLine("**********************异步方法开始了**********************");
                Thread.Sleep(2000);
                Console.WriteLine($"**********************异步方法结束了{Thread.CurrentThread.ManagedThreadId}**********************");
            };
            ThreadPool.QueueUserWorkItem(_waitCallback);

Task异步多线程

  • 不要使用线程套线程,
  • Task的妙用 Task.Run 返回执行的Task ,利用list 来存储需要执行的线程
  • 回调线程Continuewith ,回调线程Id号有可能是当前线程,也有可能创建了新的线程完成回调
             Task _task = new Task(_action);
            _task.Start();  // 这个是启用线程

            List<Task> _list_task = new List<Task>();   //这种写法好啊, 后面的task使用.ToArray来返回数组
            _list_task.Add(Task.Run(() => Console.WriteLine("chusheng 1")));
            _list_task.Add(Task.Run(() => Console.WriteLine("chusheng 2")));
            _list_task.Add(Task.Run(() => Console.WriteLine("chusheng 3")));
            _list_task.Add(Task.Run(() => Console.WriteLine("chusheng 4")));
            _list_task.Add(Task.Run(() => Console.WriteLine("chusheng 5")));
            TaskFactory _taskFactory = new TaskFactory();
            
            // TaskFactory 比较方便,他存储了一些好用的api来使用

            // ContinueWhenAny 有一个线程完成了,就执行后面的线程,同时后面的lambda表达式创建了一个新的线程,让后返回,所以这个函数本身就是一个线程
            _taskFactory.ContinueWhenAny(_list_task.ToArray(), o => { Console.WriteLine($"其中有一个线程完成了{Thread.CurrentThread.ManagedThreadId}"); });

            //ContinueWhenAll 当所以线程完成后,再执行 
            _list_task.Add(_taskFactory.ContinueWhenAll(_list_task.ToArray(), o => { Console.WriteLine($"全部线程完成了{Thread.CurrentThread.ManagedThreadId}"); }));

            // 名字是一样的,不如前面好用 
            Task.WaitAny(_list_task.ToArray());
            Console.WriteLine("项目完成一切口");
            Task.WaitAll(_list_task.ToArray());
            Console.WriteLine("任务搞完了");

多线程使用技巧

  • 情况一 多次读取,因为主线程计算太快,所以读取不到想要的值,
        for (int i = 0; i < 5; i++)
           {
               int k = i;   //这里的i和k不相同, 如果看结果你发现, i都是5, k是0,1,2,3,4,这是为什么
               // 对于i来说他运行于主线程,所以i的计算从1到5非常快,到子线程读取他的时候,就会是5,而k他是每次创建,所以有5个k;
               Task.Run(() =>
               {
                   Console.WriteLine($"start线程所处于的循环{i} {k}, 当前线程编号 {Thread.CurrentThread.ManagedThreadId}");
                   Thread.Sleep(2000);
                   Console.WriteLine($"end线程所处于的循环{i} {k}, 当前线程编号 {Thread.CurrentThread.ManagedThreadId}");
               });
           }
  • 情况二 线程的存储也会有问题
       List<int> intlist= new List<int>();
       for(int i=0;i<10000;i++){
           Task.Run(()=>intlist.Add(i));
           

       }
       Console.WriteLine(intlist.Count);  //这里并没有10000 ,而是999n个数,每次运算结果不同
  • 对于线程而言,就是读取和存储时往往出现问题,这就是线程安全,线程不安全,就是单线程顺序执行和多线程结果不同这就是线程不安全.

await/async语法

  • 首先他不是一个创建线程的方法,创建线程还是需要利用Task.run()
  • 他是c#5.0的新语法, 他的语法规则可分为 有返回和无返回
  • await 必须写在Task.run的前面,所以await本身不多线程,但是往往牵扯多线程
  • await的优势在于,await Task.run 后面的语句相当于一个回调语句,不用你自己再去写一个回调函数了,
  1. 无返回值线程语法的编写
   // 函数一 这里原来是没有返回值的,但是async语法自动返回一个Task, 所以返回值上面你要写Task
   public void task_changsh()
        {
            Console.WriteLine($"当前task_changsh主线程的线程是start: {Thread.CurrentThread.ManagedThreadId}");
            Task.Run(() =>
            {
                Console.WriteLine($"当前task_changsh子线程的线程是start: {Thread.CurrentThread.ManagedThreadId}");
                Thread.Sleep(2000);
                Console.WriteLine($"当前task_changsh子线程的线程是end: {Thread.CurrentThread.ManagedThreadId}");

            });


            Console.WriteLine($"当前task_changsh主线程的线程是end: {Thread.CurrentThread.ManagedThreadId}");

        }


        public async Task task_chanshiAsync()  //返回Task是重点
        {
            Console.WriteLine($"当前task_chanshiAsync主线程的线程是start: {Thread.CurrentThread.ManagedThreadId}");
            await Task.Run(() =>
             {
                 Console.WriteLine($"当前task_chanshiAsync子线程的线程是start: {Thread.CurrentThread.ManagedThreadId}");
                 Thread.Sleep(2000);
                 Console.WriteLine($"当前task_chanshiAsync子线程的线程是end: {Thread.CurrentThread.ManagedThreadId}");

             });
            //await 后面的语句等价于将下面语句替换成
            Console.WriteLine($"当前task_chanshiAsync主线程的线程是end: {Thread.CurrentThread.ManagedThreadId}");  //原来的语句
             _task.ContinueWith(o => { Console.WriteLine($"当前task_chanshiAsync主线程的线程是end: {Thread.CurrentThread.ManagedThreadId}"); });//替换成这个语句



        }

  1. 有返回值的线程编写
//带有返回值long类型,经过async 必须返回Task 后,就将返回值类型从long 变成 Task<long>枚举
   public long task_changsh_long()
        {
            Console.WriteLine($"当前task_changsh_long主线程的线程是start: {Thread.CurrentThread.ManagedThreadId}");
            long result = 0;
            Task.Run(() =>
            {
                Console.WriteLine($"当前task_changsh_long子线程的线程是start: {Thread.CurrentThread.ManagedThreadId}");
                for (int i = 0; i < 1000000; i++)
                {
                    result += i;
                }
                Console.WriteLine($"当前task_changsh_long子线程的线程是end: {Thread.CurrentThread.ManagedThreadId}");

            });


            Console.WriteLine($"当前task_changsh_long主线程的线程是end: {Thread.CurrentThread.ManagedThreadId}");
            return result;
        }

        public async Task<long> task_changsh_long_async()
        {
            Console.WriteLine($"当前task_changsh_long_async主线程的线程是start: {Thread.CurrentThread.ManagedThreadId}");
            long result = 0;
            await Task.Run(() =>
            {
                Console.WriteLine($"当前task_changsh_long_async子线程的线程是start: {Thread.CurrentThread.ManagedThreadId}");
                for (int i = 0; i < 1000000; i++)
                {
                    result += i;
                }
                Console.WriteLine($"当前task_changsh_long_async子线程的线程是end: {Thread.CurrentThread.ManagedThreadId}");

            });


            Console.WriteLine($"当前task_changsh_long_async主线程的线程是end: {Thread.CurrentThread.ManagedThreadId}");
            return result;  //改返回的值类型会不变,只是 async 会将返回类型long自动封装成Task<long>
        }


await函数执行的顺序

  • 源码:
  public static void Main(string[] args)
        {
            Program _program = new Program();
            _program.Fma_Task();
        }

         
         
         public void Fma_Task()
        {
            Console.WriteLine($"当前Main主线程的线程是start: {Thread.CurrentThread.ManagedThreadId}");
            task_chanshiAsync();
            Console.WriteLine($"当前Main主线程的线程是end: {Thread.CurrentThread.ManagedThreadId}");
        }


  public async void task_chanshiAsync()
        {
            Console.WriteLine($"当前task_chanshiAsync主线程的线程是start: {Thread.CurrentThread.ManagedThreadId}");
            Task _task = Task.Run(() =>
             {
                 Console.WriteLine($"当前task_chanshiAsync子线程的线程是start: {Thread.CurrentThread.ManagedThreadId}");
                 Thread.Sleep(2000);
                 Console.WriteLine($"当前task_chanshiAsync子线程的线程是end: {Thread.CurrentThread.ManagedThreadId}");

             });
            await _task;
            Console.WriteLine($"当前task_chanshiAsync主线程的线程是end: {Thread.CurrentThread.ManagedThreadId}");

        }
1. "当前Main主线程的线程是start:
2. " 当前task_chanshiAsync主线程的线程是start:
3. "当前Main主线程的线程是end:                 // 对于这里函数的调用,你就可以发现await的神奇之处,他会及时返回主线程,遇见await快速返回,并不会耽误主线程
4. "当前task_chanshiAsync子线程的线程是start:
5. "当前task_chanshiAsync子线程的线程是end: 
6. "当前task_chanshiAsync主线程的线程是end:    //  因为await语法特写,所以这里是子线程回调语句输出十分正常

标签:02,01,Console,Thread,Task,task,线程,WriteLine,多线程
From: https://www.cnblogs.com/miemienihai/p/17808201.html

相关文章

  • Windows server 2022 搭建 AD 域服务器<01>
    1.AD(ActiveDirectory)WindowsServer环境准备AD应用程序:ActiveDirectory域控制器主机名称IP角色AD-Server192.168.61.237AD服务器2.配置AD环境地址3.添加角色和功能配置域控制器配置DSRM密码:Lahmy1c!安装后会自动重启服务器,重启后,系统将......
  • 北京君正X2600处理器亮相ELEXCON 2023,打造多核异构跨界新价值
        伴随下游应用持续丰富,细节需求不断增多,标准化产品已越来越难以满足市场需求,芯片方案提供商需要不断深入行业,根据市场需求推出适配的产品。在这样的背景下,北京君正迅速推出X2600系列多核异构跨界处理器,并于2023年ELEXCON深圳国际电子展上正式推向市场。北京君正X2600系列......
  • 2023maven的最新的阿里云仓库镜像最新地址
    参考地址:https://developer.aliyun.com/mvn/guide阿里云Maven中央仓库为 阿里云云效 提供的公共代理仓库,帮助研发人员提高研发生产效率,使用阿里云Maven中央仓库作为下载源,速度更快更稳定。阿里云云效 是企业级一站式DevOps平台,覆盖产品从需求到运营的研发全生命周期,其中云......
  • NOIP2023模拟9联测30
    这篇博客是第二天赛时写的。(恼)T1数学题。肯定是想把\(k\)质因数分解,然后找一找规律,发现对于一个最小的\(n\)一定不包括除了\(k\)有的质因子以外的其他质因子,因为其他质因子对是不是\(k\)的倍数没有用。\(n^2\)相当于把\(n\)的所有质因子的指数乘了个\(2\),那么只......
  • Kafka反序列化RCE漏洞(CVE-2023-34040)
    漏洞描述SpringKafka是SpringFramework生态系统中的一个模块,用于简化在Spring应用程序中集成ApacheKafka的过程,记录(record)指Kafka消息中的一条记录。受影响版本中默认未对记录配置 ErrorHandlingDeserializer,当用户将容器属性 checkDeserExWhenKeyNull 或 chec......
  • 面试必刷TOP101:19、寻找峰值
    题目题解如输入[2,4,1,2,7,8,4]时,会形成两个山峰,一个是索引为1,峰值为4的山峰,另一个是索引为5,峰值为8的山峰,如下图所示:importjava.util.*;publicclassSolution{/***代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可***@......
  • VS2022 XAML Styler拓展工具安装失败解决办法
    引言使用VS2022拓展功能在线安装XAMLStyler工具时,会出现安装失败的问题,下面介绍如何解决。其他拓展工具如果安装失败,可以参考相同解决办法。步骤:在VS菜单中选择,Extensions->ManageExtensions在新窗口中,搜索xamlstyler,然后下载对工具下载完成后,关闭VS编译器,会自......
  • CSP-S2023 全场题解
    lock这题就是个模拟吧,赛时被迷惑了以为是什么不可做题,仔细看只有\(10^5\)种状态,那就枚举好了。我们分别从状态串出发,枚举它能达到的答案,加到set取个并集,不过注意给定的状态不能是密码,要减掉。注意不要直接计数器减减,不然如果有相同的算在状态里面的会多减,我考场代码就这么被......
  • 2024年PMI-PBA商业分析认证报考指南(全网最全)
    一、什么是商业分析师认证PMI-PBA®?PMI-PBA®是商业分析专业人士(PMI商业分析师)。PMI-PBA®强调在业务分析方面的专业知识能力。突出了分析人员与干系人之间有效工作的能力,从而定义他们的业务需求,确保项目的输出,成功完成商业成果。 商业分析已成为对项目管理至关重要的能力。作为一......
  • C++使用多线程将数据写入文件
    #include<iostream>#include<vector>#include<thread>#include<fstream>//使用多线程将数据写入文件voidwriteToFile(conststd::vector<std::string>&data,conststd::string&filename){//创建一个文件输出流std::ofstreamfile......