翼度科技»论坛 编程开发 .net 查看内容

Csharp线程

3

主题

3

帖子

9

积分

新手上路

Rank: 1

积分
9
CSharpe线程


目录

什么是进程?
一个应用程序的运行---对标于一个进程----虚拟词;
所谓的进程---记录了程序运行所消耗的各种各样的资源;
什么是线程?
就是计算机程序在运行的时候,执行指令的最小的执行流~ 程序
的运行---很多的并发操作,任何一个指令的执行都是需要通过线程来完成;
一个进程至少要包含一个线程;进程退出,线程也是自动消失;
什么是多线程?
随着技术的发展---业务的需求---需要指令的并发执行;
同时执行多种指令(线程来执行的);
和CPU的核数有关~~
C#如何操作线程


  • Thread(很少用)
  • ThreadPool(线程池)
  • Task(主流-----重点)
Thread

Thread:来自于System.Threading的一个密封类,它是在.net Framwork1.0时代出现的,在C#中用来操作计算机资源线程的一个帮助类库;
1. Thread如何开启一个线程呢?

多线程因为是无序的,调试不太好调试,只能通过写日志,输出结果,根据结果来判断thread的特点.
  1. private void btn_Thread_Click(object sender, EventArgs e)
  2. {
  3.     Debug.WriteLine($"***************Main Thread start: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  4.     Thread thread = new Thread(() =>
  5.     {
  6.         Debug.WriteLine($"***************Thread start: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  7.       
  8.         Debug.WriteLine($"***************Thread end: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  9.     });
  10.     thread.Start();
  11.     Debug.WriteLine($"***************Main Thread end: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  12. }
复制代码
结果

2. Thread中常见的API
  1. thread.Suspend(); // 线程暂停
  2. thread.Resume();  // 线程恢复
  3. thread.Abort();   // 线程终止
  4. 1.线程等待的:ThreadState有多种状态;如果线程停止了,状态会修改;
  5. while (thread.ThreadState != System.Threading.ThreadState.Stopped) //如果线程没有停止;
  6. {
  7.     Thread.Sleep(500); //当前休息500ms  不消耗计算机资源的
  8. }
  9. 2.自己支持的线程等待:
  10. thread.Join();//等待线程中的内容执行完毕;继续往后;
  11. thread.Join(500);//等待500ms,过时不候;
  12. thread.Join(TimeSpan.FromMilliseconds(500));//等待500ms,过时不候;
  13. thread.IsBackground = true;// 是后台线程:程序强制关掉,线程也就随之消失了;
  14. thread.IsBackground = false; //是前台线程:程序强制关掉,线程会等待,内部的行为执行完毕,然后才结束;
  15. thread.Start();
复制代码
3. thread的扩展封装

多线程;异步执行;
不阻塞界面;
无序性---多个动作。如果使用多线程,是无法控制顺序的。
现在有两个动作 使用了2个委托 必须是多线程执行的 要求两个委托按顺序执行。
  1. Action action = () => { Debug.WriteLine("this is first run"); };
  2. Action action2 = () => { Debug.WriteLine("this is second run"); };
  3. private void button1_Click(object sender, EventArgs e)
  4. {
  5.      callBack(action, action2);
  6. }
  7. private void callBack(Action action,Action action1)
  8. {
  9.     Thread t= new Thread(() =>
  10.     {
  11.         action();
  12.         action1();
  13.     });
  14.      t.Start();
  15. }
复制代码
如果有一个带返回值的委托,需要你要多线程执行;
  1. Func<int> func = () => { return DateTime.Now.Year; };
  2. private void button1_Click(object sender, EventArgs e)
  3. {
  4.      Func<int> func1= CallBack<int>(func);
  5.      Debug.WriteLine("t****************");
  6.      Debug.WriteLine("t****************");
  7.      Debug.WriteLine("t****************");
  8.      Debug.WriteLine("t****************");
  9.      Debug.WriteLine("t****************");
  10.      Debug.WriteLine("t****************");
  11.      int iResult=func1();
  12.      Debug.WriteLine(iResult);
  13. }   
  14. private Func<T> CallBack<T>(Func<T> func)
  15. {
  16.      T result = default(T);
  17.      Thread t = new Thread(() =>
  18.      {
  19.          result = func();
  20.      });
  21.      t.Start();
  22.      return new Func<T>(() => {
  23.          t.Join();
  24.          return result; });
  25.    
  26. }
复制代码
threadpool

Thread对比Threadpool:Api很多,功能繁多;使用起来,不好控制;让开发者试用起来并不友好;
Thread对线程的数量管控,全部都需要让程序员自己来管控;
一、 .NET Framework2.0时代:出现了一个线程池ThreadPool

是一种池化思想,相当于是在池子中,有线程存在;如果需要使用线程;就可以直接到线程池中去获取直接使用,如果使用完毕,在自动的回放到线程池中去;
好处:
1.不需要程序员对线程的数量管控,提高性能,放置滥用
2.去掉了很多在Thread中没有必要的Api
二、线程池如何申请一个线程呢?
  1. ThreadPool.QueueUserWorkItem((state) =>
  2. {
  3.      Debug.WriteLine($"***************ThreadPool start: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  4.      Thread.Sleep(5000);
  5.      Debug.WriteLine($"***************ThreadPool end: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  6. });
复制代码
三、线程等待


  • 观望式的:
  • 定义一个监听ManualResetEvent
  • 通过ManualResetEvent.WaitOne等待
  • 等到ManualResetEvent.Set方法执行,方法执行完毕后,主线程就继续往后;
  1.             ManualResetEvent manualResetEvent = new ManualResetEvent(false);
  2.             ThreadPool.QueueUserWorkItem((state) =>
  3.             {
  4.                 Debug.WriteLine($"***************ThreadPool start: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  5.                 Thread.Sleep(5000);
  6.                 Debug.WriteLine($"***************ThreadPool end: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  7.                 manualResetEvent.Set();
  8.             });
  9.             manualResetEvent.WaitOne();
  10.             Debug.WriteLine($"***************Main Thread end: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  11.         }
复制代码
四、线程池如何控制线程数量

如果通过SetMinThreads/SetMaxThreads来设置线程的数量;这个数量访问是在当前进程是全局的;
  1. {
  2.      int workerThreads = 4;
  3.      int completionPortThreads = 4;
  4.      ThreadPool.SetMinThreads(workerThreads, completionPortThreads);
  5. }
  6. {
  7.      int workerThreads = 8;
  8.      int completionPortThreads = 8;
  9.      ThreadPool.SetMaxThreads(workerThreads, completionPortThreads);
  10. }
  11. {
  12.      ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);
  13.      Debug.WriteLine($"当前进程最小的工作线程数量:{workerThreads}");
  14.      Debug.WriteLine($"当前进程最小的IO线程数量:{completionPortThreads}");
  15. }
  16. {
  17.      ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
  18.      Debug.WriteLine($"当前进程最大的工作线程数量:{workerThreads}");
  19.      Debug.WriteLine($"当前进程最大的IO线程数量:{completionPortThreads}");
  20. }
复制代码
Task

一、Task开启线程有哪些方式
  1. Action action = () =>
  2. {
  3.     Console.WriteLine($"***************Task start: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  4.     Console.WriteLine("启动了一个新的线程");
  5.     Console.WriteLine($"***************Task end: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  6. };
  7. Task task = new Task(action);
  8. task.Start();
  9. Task.Run (() =>
  10. {
  11.     Console.WriteLine($"***************Task.Run start: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  12.     Console.WriteLine("启动了一个新的线程");
  13.     Console.WriteLine($"***************Task.Run end: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  14. });
  15. TaskFactory taskFactory = new TaskFactory();
  16. taskFactory.StartNew(() =>
  17. {
  18.     Console.WriteLine($"***************TaskFactory start: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  19.     Console.WriteLine("启动了一个新的线程");
  20.     Console.WriteLine($"***************TaskFactory end: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  21. });
  22. Task.Factory.StartNew(() =>
  23. {
  24.     Console.WriteLine($"***************Task.Factory start: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  25.     Console.WriteLine("启动了一个新的线程");
  26.     Console.WriteLine($"***************Task.Factory end: {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("HH:mm:ss.fff")} **************** ");
  27. });
复制代码
启动的多线程的特点:

  • 不阻塞主线程----不会卡顿界面
  • 线程的启动---由操作系统来调度启动;  延迟启动(延迟很短)
  • 并发执行~~
线程执行完毕就销毁了吗?
ThreadPool   线程池----Task 线程都是来自于线程池的;
多进程技术的使用场景的分析

问题:尽可能的多启动线程??      万万不可的,一定要适当的使用;
一堆业务逻辑:   项目要开发   10个板块
单线程执行:  一个人去承担这个项目开发----一步一步的做;一个版快一个板块的去开发;     开发周期时间长
多线程执行:  一个团队开发: 效率更高~~  多个人可以分工开发;
类比:        一个人(开支小)和一个团队(10个人  10份工资);
线程等待

有Delay 和 Sleep两种方式来进行线程的等待.
  1. {
  2.     Stopwatch stopwatch = new Stopwatch();
  3.     stopwatch.Start();
  4.     Task.Delay(3000);
  5.     stopwatch.Stop();
  6.     Console.WriteLine($"time:{stopwatch.ElapsedMilliseconds}");
  7. }
  8. {
  9.     Stopwatch stopwatch = new Stopwatch();
  10.     stopwatch.Start();
  11.     Thread.Sleep(3000);
  12.     stopwatch.Stop();
  13.     Console.WriteLine($"time:{stopwatch.ElapsedMilliseconds}");
  14. }
复制代码
结果为:

Task.Delay().ContinueWith() 不阻塞主线程,等待多长时间之后,可以执行一段业务逻辑----回调函数
Thread.Sleep() 阻塞主线程,主线程等待指定时间后再运行。
线程等待的多种方案
  1.   Task<int> task = Task.Run(() =>
  2.   {
  3.       Thread.Sleep(3000);
  4.       Console.WriteLine("Open new thread!");
  5.       return 10;
  6.   });
  7.   int num = task.Result; //等待task执行完毕,获取返回值,会阻塞当前线程
  8.    //下面是没有返回值方法调用的时候,使用的方法
  9.      //Task.WaitAll(task); //等待task执行完毕,会阻塞当前线程
  10.     //int i = Task.WaitAny(task); //等待task执行完毕,会阻塞当前线程
复制代码
什么场景下可以使用多线程呢?(可以并发的时候)    不适合使用多线程??
故事: 高级班的项目实战---逐个讲解知识点,然后项目实战,分工合作,分小组开发;

  • 逐个讲解知识点                   -----可以多线程来模拟?---只有Richard老师一个人讲解----不可用;不能并发,不能多线程来模拟
  • 项目实战,分工合作,分小组开发; -----可以多线程来模拟?---有多个人同时开发,可以分工并发开发,可以多线程开发~~
模拟的代码
  1. /// <summary>
  2. /// 模拟讲课的方法
  3. /// </summary>
  4. /// <param name="lesson">课程名</param>
  5. private void Tech(string lesson)
  6. {
  7.      Console.WriteLine($"{lesson} ||开始了.....");
  8.      long iResult = 0;
  9.      for (int i = 0;i<1_000_000_000;i++)
  10.      {
  11.          iResult += i;
  12.      }
  13.      Console.WriteLine($"{lesson} ||讲完了.....");
  14. }
  15. /// <summary>
  16. /// 模拟不同人开发的方法
  17. /// </summary>
  18. /// <param name="name"></param>
  19. /// <param name="projectName"></param>
  20. private void Coding(string name,string projectName)
  21. {
  22.      Console.WriteLine($"************************* Coding Start || {name}  {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ****************");
  23.      long iResult = 0;
  24.      for (int i = 0; i < 1_000_000_000; i++)
  25.      {
  26.          iResult += i;
  27.      }
  28.      Console.WriteLine($"************************* Coding End || {name}  {projectName} {Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ****************");
  29. }
复制代码
需求一、所有人的任务都执行完成后,小聚一下,大吃一顿```
  1. private void button5_Click(object sender, EventArgs e)
  2. {
  3.     Console.WriteLine("同学们!开始上课了");
  4.     Tech("泛型");
  5.     Tech("委托");
  6.     Tech("多线程");
  7.     Tech("异步编程");
  8.     Tech("并发编程");
  9.     Console.WriteLine("知识点讲解完毕了~~开始项目实战开发`~~~");
  10.     TaskFactory factory = new TaskFactory();
  11.     factory.StartNew(() => Coding("张三", "数据库设计"));
  12.     factory.StartNew(() => Coding("李四", "框架的搭建"));
  13.     factory.StartNew(() => Coding("王五", "Wechat Pay"));
  14.     factory.StartNew(() => Coding("赵六", "Web Api"));
  15.     factory.StartNew(() => Coding("田七", "封装通用的组件"));               
  16.     factory.StartNew(() => Coding("刘八", "编译"));
  17.     factory.StartNew(() => Coding("杨九", "发行"));
  18.    
  19. }
复制代码
需求2、 开发人员中,只要其中有一个执行完成了,Richard老师就准备发布环境,准备发布部署
  1.   private void button5_Click(object sender, EventArgs e)
  2.   {
  3.       List<Task> tasks = new List<Task>();
  4.       Console.WriteLine("同学们!开始上课了");
  5.       Tech("泛型");
  6.       Tech("委托");
  7.       Tech("多线程");
  8.       Tech("异步编程");
  9.       Tech("并发编程");
  10.       Console.WriteLine("知识点讲解完毕了~~开始项目实战开发`~~~");
  11.       TaskFactory factory = new TaskFactory();
  12.       tasks.Add( factory.StartNew(() => Coding("张三", "数据库设计")));
  13.       tasks.Add(factory.StartNew(() => Coding("李四", "框架的搭建")));
  14.       tasks.Add(factory.StartNew(() => Coding("王五", "Wechat Pay")));
  15.       tasks.Add(factory.StartNew(() => Coding("赵六", "Web Api")));
  16.       tasks.Add(factory.StartNew(() => Coding("田七", "封装通用的组件")));
  17.       tasks.Add(factory.StartNew(() => Coding("刘八", "编译")));
  18.       tasks.Add(factory.StartNew(() => Coding("杨九", "发行")));
  19.       Task.WaitAll(tasks.ToArray());
  20.       Console.WriteLine("项目开发完毕了~~~,去大吃一顿~~");      
  21.   }
复制代码
使用场景:
Task.WaitAll----系统首页---包含了很多的信息---都是后台提供----获取这个结果的时候;准备一个复杂实体---包含各种信息  查询这些数据---可以多线程去执行;同时查询;
查询必须要获取到所有的数据----要获取所有的数据----Task.WaitAll
Task.WaitAny----查询一条数据----数据来源可能是不同的地方,数据库/缓存/接口/读取硬盘中的数据
1.传统做法: 先查询缓存试试看,如果没有,再查询数据库,如果没有,再继续往后,直到查询到数据为止;
2.有四个渠道获取数据----> 只要有一个渠道获取到数据就Ok, 直接启动四个线程去查询; 等待其中有一个线程执行完成,特殊处理,如果查询到数据后,就结束~~ 只要有一个执行结束了,就已经拿到数据了,其他的不用管了~~
需求3、 有没有可以不阻塞主线程,也能达到效果;
  1. private void button5_Click(object sender, EventArgs e)
  2. {
  3.      List<Task> tasks = new List<Task>();
  4.      Console.WriteLine("同学们!开始上课了");
  5.      Tech("泛型");
  6.      Tech("委托");
  7.      Tech("多线程");
  8.      Tech("异步编程");
  9.      Tech("并发编程");
  10.      Console.WriteLine("知识点讲解完毕了~~开始项目实战开发`~~~");
  11.      TaskFactory factory = new TaskFactory();
  12.      tasks.Add( factory.StartNew(() => Coding("张三", "数据库设计")));
  13.      tasks.Add(factory.StartNew(() => Coding("李四", "框架的搭建")));
  14.      tasks.Add(factory.StartNew(() => Coding("王五", "Wechat Pay")));
  15.      tasks.Add(factory.StartNew(() => Coding("赵六", "Web Api")));
  16.      tasks.Add(factory.StartNew(() => Coding("田七", "封装通用的组件")));
  17.      tasks.Add(factory.StartNew(() => Coding("刘八", "编译")));
  18.      tasks.Add(factory.StartNew(() => Coding("杨九", "发行")));
  19.      {
  20.          Task.WaitAny(tasks.ToArray());  //等待一堆任务中,其中有一个执行完成了,继续往后执行~
  21.          Console.WriteLine("XXX 完成了开发任务~~,Richard老师就准备发布环境,准备发布部署");
  22.      }
  23.      {
  24.          Task.WaitAll(tasks.ToArray());
  25.          Console.WriteLine("项目开发完毕了~~~,去大吃一顿~~");
  26.      }
  27.      
  28. }
复制代码
需求4、如果想要完成以上需求,要求不阻塞主线程,如果也没有ContinueWhenAll api.
  1. private void button2_Click(object sender, EventArgs e)
  2. {
  3.      List<Task> tasks = new List<Task>();
  4.      Console.WriteLine("同学们!开始上课了");
  5.      Tech("泛型");
  6.      Tech("委托");
  7.      Tech("多线程");
  8.      Tech("异步编程");
  9.      Tech("并发编程");
  10.      Console.WriteLine("知识点讲解完毕了~~开始项目实战开发`~~~");
  11.      TaskFactory factory = new TaskFactory();
  12.      tasks.Add(factory.StartNew(Object => Coding("张三", "数据库设计"),"张三"));
  13.      tasks.Add(factory.StartNew(Object => Coding("李四", "框架的搭建"), "李四"));
  14.      tasks.Add(factory.StartNew(Object => Coding("王五", "Wechat Pay"), "王五"));
  15.      tasks.Add(factory.StartNew(Object => Coding("赵六", "Web Api"), "赵六"));
  16.      tasks.Add(factory.StartNew(Object => Coding("田七", "封装通用的组件"), "田七"));
  17.      tasks.Add(factory.StartNew(Object => Coding("刘八", "编译"), "刘八"));
  18.      tasks.Add(factory.StartNew(Object => Coding("杨九", "发行"), "杨九"));
  19.    
  20.      {
  21.          factory.ContinueWhenAny(tasks.ToArray(), (task) =>
  22.          {
  23.              Console.WriteLine($"{task.AsyncState} 完成了开发任务~~,发一个小红包");
  24.          });
  25.      }
  26.      {
  27.          factory.ContinueWhenAll(tasks.ToArray(), (task) =>
  28.          {
  29.              Console.WriteLine("项目开发完毕了~~~,去大吃一顿~~");
  30.          });
  31.          
  32.      }
  33. }
复制代码
通过Task返回一个字符串
  1. Task.Run(() =>
  2. {
  3.     Task.WaitAll(tasks.ToArray());
  4.     Console.WriteLine("项目开发完毕了~~~,去大吃一顿~~");
  5. });
复制代码
Paralell

如何批量开启10个线程?
  1. {
  2.     List<Task<string>> tasklist = new List<Task<string>>();
  3.     for (int i = 0; i < 3; i++)
  4.     {
  5.         string k = $"{i}";
  6.         tasklist.Add(Task.Run(() =>
  7.         {
  8.             return $"{k}_Task";
  9.         }));
  10.     }
  11.     Task.Run(() =>
  12.     {
  13.         Task.WaitAny(tasklist.ToArray());
  14.         Task<string> task = tasklist.First(c => c.Status == TaskStatus.RanToCompletion);
  15.         Console.WriteLine(task.Result);
  16.     });
  17. }
复制代码
如何控制启动线程的数量?
  1. Parallel.For(0, 10, (i) =>
  2. {
  3.   
  4. Console.WriteLine($"Thread id : {Thread.CurrentThread.ManagedThreadId.ToString("00")  }") ;
  5. });
复制代码
线程异常处理

1.try_catch捕获不到多线程内部的异常.
按照正常的Try Catch来处理异常。
  1. ParallelOptions parallelOptions = new ParallelOptions();
  2. parallelOptions.MaxDegreeOfParallelism = 10;
  3. Parallel.For(0, 10100, parallelOptions, (i) =>
  4. {
  5.      Console.WriteLine($"Thread id : {Thread.CurrentThread.ManagedThreadId.ToString("00")  }");
  6. });
复制代码
2.如何捕捉线程内部的异常,try-catch  包裹,线程等待; 可以捕捉到AggregateException类型的异常;
3.一个try可以对应多个catch   发生异常后,catch捕捉,是从上往下匹配异常类型,只要是匹配到异常类型后,就进入开始处理异常;
4.如何输出消息, 要转换成AggregateException,获取InnerExceptions 的集合,多线程发生的多个异常,都在这个集合中;
  1. try {
  2.     for (int i = 0; i < 20; i++)
  3.     {
  4.         string str = $"Advance_{i}";
  5.         Task.Run(() =>
  6.         {
  7.             if (str.Equals("Advance_7"))
  8.             {
  9.                 throw new Exception("Advance_7异常");
  10.             }
  11.             else if (str.Equals("Advance_10"))
  12.             {
  13.                 throw new Exception("Advance_{10}异常");
  14.             }
  15.             else if (str.Equals("Advance_15"))
  16.             {
  17.                 throw new Exception("Advance_15异常");
  18.             }
  19.             else if (str.Equals("Advance_18"))
  20.             {
  21.                 throw new Exception("Advance_18异常");
  22.             }
  23.             else
  24.             {
  25.                 Console.WriteLine(str);
  26.             }
  27.         });
  28.     }
  29. }
  30. catch (Exception ex)
  31. {
  32.     Console.WriteLine(ex.Message);
  33. }
复制代码
线程取消

有一个需求:
首页---数据块---考情/周top10/月top ......
启动四个线程去获取数据,要正常展示----一定要四个线程都能正常获取到数据,必然要等待四个线程都执行结束;
场景:四个线程,有某一个线程异常了~~   整块数据不能用;  如果有异常,其他的正常的线程,其实查询也没有价值,既然没有异常的线程执行也没价值,就应该取消-----(因为线程在执行业务逻辑---需要消耗计算机的资源,计算机的资源是有限的)
标准方案:
定义一个cts,包含一个IsCancellationRequested 属性,默认值为=false,同时提供了一个Cancel方法, IsCancellationRequested: 默认的false ----true;  IsCancellationRequested 属性 只能通过Cancel来变化,不能通过其他的渠道修改;
  1.   private void button4_Click(object sender, EventArgs e)
  2.   {
  3.       List<Task> tasks = new List<Task>();
  4.       try {
  5.           for (int i = 0; i < 20; i++)
  6.           {
  7.               string str = $"Advance_{i}";
  8.               Task task = Task.Run(() =>
  9.               {
  10.                   if (str.Equals("Advance_7"))
  11.                   {
  12.                       throw new Exception("Advance_7异常");
  13.                   }
  14.                   else if (str.Equals("Advance_10"))
  15.                   {
  16.                       throw new Exception("Advance_{10}异常");
  17.                   }
  18.                   else if (str.Equals("Advance_15"))
  19.                   {
  20.                       throw new Exception("Advance_15异常");
  21.                   }
  22.                   else if (str.Equals("Advance_18"))
  23.                   {
  24.                       throw new Exception("Advance_18异常");
  25.                   }
  26.                   else
  27.                   {
  28.                       Console.WriteLine(str);
  29.                   }
  30.               });
  31.               tasks.Add(task);
  32.           }
  33.           Task.WaitAll(tasks.ToArray());
  34.       }
  35.       catch (Exception ex)
  36.       {
  37.           Console.WriteLine(ex.Message);
  38.       }
  39.       
  40.   }
复制代码
多线程的中间变量

先看一段代码
  1. CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
  2. try {
  3.      for (int i = 0; i < 50; i++)
  4.      {
  5.          string str = $"Advance_{i}";
  6.          Task.Run(() =>
  7.          {
  8.              if (cancellationTokenSource.IsCancellationRequested == false)
  9.              {
  10.                  Console.WriteLine("正常运行");
  11.                  if (str.Equals("Advance_7"))
  12.                  {
  13.                      cancellationTokenSource.Cancel();
  14.                      throw new Exception("Advance_7异常");
  15.                  }
  16.                  else if (str.Equals("Advance_10"))
  17.                  {
  18.                      cancellationTokenSource.Cancel();
  19.                      throw new Exception("Advance_{10}异常");
  20.                  }
  21.                  else if (str.Equals("Advance_15"))
  22.                  {
  23.                      cancellationTokenSource.Cancel();
  24.                      throw new Exception("Advance_15异常");
  25.                  }
  26.                  else if (str.Equals("Advance_18"))
  27.                  {
  28.                      cancellationTokenSource.Cancel();
  29.                      throw new Exception("Advance_18异常");
  30.                  }
  31.              }
  32.              else
  33.              {
  34.                  Console.WriteLine("线程非正常退出");
  35.              }
  36.          });
  37.          
  38.      }
  39.      
  40. }
  41. catch (Exception ex)
  42. {
  43.      Console.WriteLine(ex.Message);
  44. }
复制代码
输出的都是10000
为什么会这样那:
int i = 0;  开始循环,定义好的一个变量;
线程是延迟启动,启动线程不阻塞UI线程; 多线程要执行逻辑,要使用i,i已经是20了;
要实现我们的目的
  1. for (int i = 0; i < 10000; i++)
  2. {
  3.     Task.Run(() => Console.WriteLine($"{i}"));
  4. }
复制代码
线程安全

线程不安全:多线程在执行业务逻辑的时候,得到的结果,如果和单线程执行的结果如果不一致,那就是线程不安全~~
线程安全:单线程执行的结果要和多线程执行的结果要一致;线程安全的;
有多线程不安全的代码
  1. for (int i = 0; i < 10000; i++)
  2. {
  3.      int k = i;
  4.      Task.Run(() => Console.WriteLine($"{k}"));
  5. }
复制代码
如何解决线程安全呢?

  • 锁, ----控制执行的线程只能有一个
  • 直接使用单线程;
  • 使用线程安全对象  看看数据结构 线程安全对象  List/Arraylist 都不是线程安全的集合--把list  Arraylist 换成安全对象;
  • 通过算法+拆分做到---划块操作数据;   原理:还是单线程去操作一块数据;
  1.   private void button6_Click(object sender, EventArgs e)
  2.   {
  3.       List<int> list = new List<int>();
  4.       List<Task> tasks = new List<Task>();
  5.       for (int i = 0; i < 10000; i++)
  6.       {
  7.           tasks.Add(Task.Run(() => { list.Add(i); }));
  8.       }
  9.       Task.WaitAll(tasks.ToArray());
  10.       Console.WriteLine(list.Count);
  11.   }
复制代码
来源:https://www.cnblogs.com/wenlong-4613615/p/18101331
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
来自手机

举报 回复 使用道具