当前位置: 首页 > news >正文

专业设计服务网站智能化建设网站

专业设计服务网站,智能化建设网站,东莞网站建设推广费用,wordpress能放视频教程C#高级–多线程详解 零、文章目录 一、什么是多线程 1、进程 当一个程序开始运行时#xff0c;它就是一个进程#xff0c;进程包括运行中的程序和程序所使用到的内存和系统资源。 而一个进程又是由多个线程所组成的。 2、线程 线程是程序中的一个执行流#xff0c;每个…C#高级–多线程详解 零、文章目录 一、什么是多线程 1、进程 当一个程序开始运行时它就是一个进程进程包括运行中的程序和程序所使用到的内存和系统资源。 而一个进程又是由多个线程所组成的。 2、线程 线程是程序中的一个执行流每个线程都有自己的专有寄存器(栈指针、程序计数器等)但代码区是共享的即不同的线程可以执行同样的函数。 3、句柄 句柄是Windows系统中对象或实例的标识这些对象包括模块、应用程序实例、窗口、控制、位图、GDI对象、资源、文件等。 4、多线程 1多线程概念 多线程是指程序中包含多个执行流即在一个程序中可以同时运行多个不同的线程来执行不同的任务也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。 2多线程优点 可以提高CPU的利用率。在多线程程序中一个线程必须等待的时候CPU可以运行其它的线程而不是等待这样就大大提高了程序的效率。牺牲空间计算资源来换取时间 3多线程缺点 线程也是程序所以线程运行需要占用计算机资源线程越多占用资源也越多。占内存多多线程需要协调和管理所以需要CPU跟踪线程消耗CPU资源。占cpu多线程之间对共享资源的访问会相互影响必须解决竞用共享资源的问题。多线程存在资源共享问题线程太多会导致控制太复杂最终可能造成很多Bug。管理麻烦容易产生bug 4为什么计算机可以多线程 程序运行需要计算机资源操作系统就会去申请CPU来处理CPU在执行动作的时候是分片执行的。分片把CPU的处理能力进行切分操作系统在调度的时候按照切片去处理不同的计算需求按照规则分配切片计算资源对于同一个计算机核心来讲所有的运行都是串行的但是因为分片的存在感觉几个程序同时在向前推进。 5何时建议使用多线程 当主线程试图执行冗长的操作但系统会卡界面体验非常不好这时候可以开辟一个新线程来处理这项冗长的工作。当请求别的数据库服务器、业务服务器等可以开辟一个新线程让主线程继续干别的事。利用多线程拆分复杂运算提高计算速度。 6何时不建议使用多线程 当单线程能很好解决就不要为了使用多线程而用多线程。 5、同步异步 1同步方法 线性执行从上往下依次执行同步方法执行慢消耗的计算机资源少。 2异步方法 线程和线程之间不再线型执行多个线程总的耗时少执行快消耗的计算机资源多各线程执行是无序的。 6、C#中的多线程 Thread/ThreadPool/Task 都是C#语言在操作计算机的资源时封装的帮助类库。 二、Thread Thread是.Net最早的多线程处理方式它出现在.Net1.0时代虽然现在已逐渐被微软所抛弃微软强烈推荐使用Task但从多线程完整性的角度上来说我们有必要了解下早期多线程的是怎么处理的以便体会.Net体系中多线程处理方式的进化。 1、如何开启新线程 分析Thread类的源码发现其构造函数参数有两类 ThreadStart类是无参无返回值的委托。ParameterizedThreadStart类是有一个object类型参数但无返回值的委托。 开启了一个新的线程1 ParameterizedThreadStart parameterizedThreadStart new ParameterizedThreadStart((oInstacnce) {Debug.WriteLine($ParameterizedThreadStart--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString( HH:mm:ss.fff)}); }); Thread thread new Thread(parameterizedThreadStart); thread.Start();开启了一个新的线程2 ThreadStart threadStart new ThreadStart(() {this.DoSomething(张三); }); Thread thread new Thread(threadStart); thread.Start();2、线程的停止等待 Thread thread new Thread(() {this.DoSomething(张三, 5000); }); thread.Start(); //thread.Suspend(); //表示线程暂停现在已弃用NetCore平台已经不支持 //thread.Resume(); //线程恢复执行弃用NetCore平台已经不支持 //thread.Abort(); //线程停止子线程对外抛出了一个异常线程是无法从外部去终止的 //Thread.ResetAbort();//停止的线程继续去执行//thread.ThreadState //根据线程状态ThreadState判断实现线程间歇性休息 //while (thread.ThreadState ! System.Threading.ThreadState.Stopped) //{ // Thread.Sleep(500); //当前休息500ms不消耗计算机资源的 //}thread.Join();//主线程等待直到当前线程执行完毕 //thread.Join(500);//主线程等待500毫秒不管当前线程执行是否完毕都继续往后执行 //thread.Join(new TimeSpan(500*10000));//主线程等待500毫秒不管当前线程执行是否完毕都继续往后执行 //TimeSpan 单位100纳秒 1毫秒10000*100纳秒3、后台线程前台线程 1后台线程界面关闭线程也就随之消失 2前台线程界面关闭线程会等待执行完才结束 Thread thread new Thread(() {this.DoSomething(张三); }); thread.Start(); thread.IsBackground true;//后台线程界面关闭线程也就随之消失 thread.IsBackground false;//前台线程界面关闭线程会等待执行完才结束 thread.Start();4、跨线程操作主线程UI Thread thread new Thread(() {for (int i 0; i 5; i){Thread.Sleep(500);textBox1.Invoke(new Action(() textBox1.Text i.ToString()));} }); thread.Start();5、线程的优先级 设置优先级只是提高了他被优先执行的概率 Thread thread new Thread(() {this.DoSomething(张三); }); // 线程的优先级最高 thread.Priority ThreadPriority.Highest;Thread thread1 new Thread(() {this.DoSomething(张三); }); // 线程的优先级最低 thread1.Priority ThreadPriority.Lowest; thread.Start(); thread1.Start();//线程开启后根据优先级来执行6、扩展封装 1实现两个委托多线程顺序执行 方法封装 private void CallBackThread(Action action1, Action action2) {Thread thread new Thread(() {action1.Invoke();action2.Invoke();});thread.Start(); }方法调用 Action action1 () {this.DoSomething(张三); }; Action action2 () {this.DoSomething(李四); }; //不会阻塞线程 CallBackThread(action1, action2);运行结果 DoSomething--Start--张三--06--16:57:21.411 DoSomething--End--张三--06--16:57:23.412 DoSomething--Start--李四--06--16:57:23.417 DoSomething--End--李四--06--16:57:25.4232实现获取多线程执行委托的结果 方法封装 private FuncT CallBackFuncT(FuncT func) {T t default(T);ThreadStart threadStart new ThreadStart(() {t func.Invoke();});Thread thread new Thread(threadStart);thread.Start();return new FuncT(() {thread.Join();//等待thread执行完成return t;}); }方法调用 Funcint func () {this.DoSomething(王五);return DateTime.Now.Year; }; //这一步不会阻塞界面 Funcint func1 this.CallBackFuncint(func); Debug.WriteLine(线程开启后计算结果出来前); //这里会等待线程计算出结果才继续往后执行 int iResult func1.Invoke(); Debug.WriteLine($计算结果{iResult});运行结果 线程开启后计算结果出来前 DoSomething--Start--王五--09--17:06:12.088 DoSomething--End--王五--09--17:06:14.090 计算结果20217、数据槽 为了解决多线程竞用共享资源的问题引入数据槽的概念即将数据存放到线程的环境块中使该数据只能单一线程访问。 1AllocateNamedDataSlot命名槽位和AllocateDataSlot未命名槽位 在主线程上设置槽位使该数据只能被主线程读取其它线程无法访问 AllocateNamedDataSlot命名槽位 var d Thread.AllocateNamedDataSlot(userName); Thread.SetData(d, 张三); //声明一个子线程 var t1 new Thread(() {Debug.WriteLine($子线程中读取数据{Thread.GetData(d)}); }); t1.Start(); //主线程中读取数据 Debug.WriteLine($主线程中读取数据{Thread.GetData(d)});AllocateDataSlot未命名槽位 var d Thread.AllocateDataSlot(); Thread.SetData(d, 李四); //声明一个子线程 var t1 new Thread(() {Debug.WriteLine($子线程中读取数据{Thread.GetData(d)}); }); t1.Start(); //主线程中读取数据 Debug.WriteLine($主线程中读取数据{Thread.GetData(d)});运行结果 主线程中读取数据张三 子线程中读取数据 主线程中读取数据李四 子线程中读取数据2利用特性[ThreadStatic] 在主线程中给ThreadStatic特性标注的变量赋值则只有主线程能访问该变量 变量标记特性 [ThreadStatic] private static string Age string.Empty;线程访问变量 Age 小女子年方28; //声明一个子线程 var t1 new Thread(() {Debug.WriteLine($子线程中读取数据{Age}); }); t1.Start(); //主线程中读取数据 Debug.WriteLine($主线程中读取数据{Age});运行结果 主线程中读取数据小女子年方28 子线程中读取数据3利用ThreadLocal线程的本地存储 在主线程中声明ThreadLocal变量并对其赋值则只有主线程能访问该变量 程序访问 ThreadLocalstring tlocalSex new ThreadLocalstring(); tlocalSex.Value 女博士; //声明一个子线程 var t1 new Thread(() {Debug.WriteLine($子线程中读取数据{tlocalSex.Value}); }); t1.Start(); //主线程中读取数据 Debug.WriteLine($主线程中读取数据{tlocalSex.Value});运行结果 主线程中读取数据女博士 子线程中读取数据8、内存栅栏 当多个线程共享一个变量的时候在Release模式的优化下子线程会将共享变量加载到cup Cache中导致主线程不能使用该变量而无法运行 1默认情况(Release模式主线程不能正常运行) var isStop false; var t new Thread(() {var isSuccess false;while (!isStop){isSuccess !isSuccess;}Trace.WriteLine(子线程执行成功); }); t.Start(); Thread.Sleep(1000); isStop true; t.Join(); Trace.WriteLine(主线程执行结束);2MemoryBarrier解决共享变量Release模式下可以正常运行 在此方法之前的内存写入都要及时从cpu cache中更新到memory在此方法之后的内存读取都要从memory中读取而不是cpu cache。 var isStop false; var t new Thread(() {var isSuccess false;while (!isStop){Thread.MemoryBarrier();isSuccess !isSuccess;}Trace.WriteLine(子线程执行成功); }); t.Start(); Thread.Sleep(1000); isStop true; t.Join(); Trace.WriteLine(主线程执行结束);3VolatileRead解决共享变量Release模式下可以正常运行 var isStop 0; var t new Thread(() {var isSuccess false;while (isStop 0){Thread.VolatileRead(ref isStop);isSuccess !isSuccess;}Trace.WriteLine(子线程执行成功); }); t.Start(); Thread.Sleep(1000); isStop 1; t.Join(); Trace.WriteLine(主线程执行结束);三、ThreadPool .NET Framework2.0时代出现了一个线程池ThreadPool是一种池化思想如果需要使用线程就可以直接到线程池中去获取直接使用如果使用完毕在自动的回放到线程池去 1、ThreadPool好处 不需要程序员对线程的数量管控,提高性能防止滥用去掉了很多在Thread中没有必要的Api 2、线程池如何分配一个线程 QueueUserWorkItem方法将方法排入队列以便开启异步线程它有两个重载。 QueueUserWorkItem(WaitCallback callBack)WaitCallback是一个有一个object类型参数且无返回值的委托。QueueUserWorkItem(WaitCallback callBack, object state)WaitCallback是一个有一个object类型参数且无返回值的委托state即WaitCallback中需要的参数 不推荐这么使用存在拆箱装箱的转换问题影响性能。 //无参数 ThreadPool.QueueUserWorkItem(o this.DoSomething(张三)); //一个参数 ThreadPool.QueueUserWorkItem(o this.DoSomething(张三), 12345);3、线程等待 1定义一个监听ManualResetEvent 2通过ManualResetEvent.WaitOne等待 3等到ManualResetEvent.Set方法执行了主线程等待的这个WaitOne()就继续往后执行 ManualResetEvent resetEvent new ManualResetEvent(false); ThreadPool.QueueUserWorkItem(o {this.DoSomething(o.ToString());resetEvent.Set(); }, 张三); resetEvent.WaitOne();4、线程池如何控制线程数量 如果通过SetMinThreads/SetMaxThreads来设置线程的数量不建议大家去这样控制线程数量这个数量访问是在当前进程中是全局的错误配置可能影响程序的正常运行 {//线程池中的工作线程数int workerThreads 4;//线程池中异步 I/O 线程的数目int completionPortThreads 4;//设置最小数量ThreadPool.SetMinThreads(workerThreads, completionPortThreads); } {int workerThreads 8;int completionPortThreads 8;//设置最大数量ThreadPool.SetMaxThreads(workerThreads, completionPortThreads); } {ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);Debug.WriteLine($当前进程最小的工作线程数量{workerThreads});Debug.WriteLine($当前进程最小的IO线程数量{completionPortThreads}); } {ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);Debug.WriteLine($当前进程最大的工作线程数量{workerThreads});Debug.WriteLine($当前进程最大的IO线程数量{completionPortThreads}); }5、扩展一个定时器功能 1RegisterWaitForSingleObject类但是不常用.涉及到定时任务建议使用Quartz.Net 2System.threading命名空间下的Thread类通过查看源码构造函数中有四个参数 第一个是object参数的委托第二个是委托需要的值第三个是调用 callback 之前延迟的时间量以毫秒为单位第四个是 调用 callback 的时间间隔以毫秒为单位 //每隔3s开启一个线程执行业务逻辑 ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(true), new WaitOrTimerCallback((obj, b) this.DoSomething(张三)), hello world, 3000, false); //效果类似于Timer定时器2秒后开启该线程然后每隔3s调用一次 System.Threading.Timer timer new System.Threading.Timer((n) this.DoSomething(李四), 1, 2000, 3000);四、Task 1、Task出现背景 在前面的章节介绍过Task出现之前微软的多线程处理方式有Thread→ThreadPool→委托的异步调用虽然也可以基本业务需要的多线程场景但它们在多个线程的等待处理方面、资源占用方面、线程延续和阻塞方面、线程的取消方面等都显得比较笨拙在面对复杂的业务场景下显得有点捉襟见肘了。正是在这种背景下Task应运而生。 Task是微软在.Net 4.0时代推出来的也是微软极力推荐的一种多线程的处理方式Task看起来像一个Thread实际上它是在ThreadPool的基础上进行的封装Task的控制和扩展性很强在线程的延续、阻塞、取消、超时等方面远胜于Thread和ThreadPool。 2、Task开启线程有哪些方式 1new Task().Start() Task task new Task(() {this.DoSomething(张三); }); task.Start(); Taskint task2 new Taskint(() {return DateTime.Now.Year; }); task2.Start(); int result task2.Result; Debug.WriteLine($result{result});2Task.Run() Task.Run(() {this.DoSomething(张三); }); Taskint task2 Task.Runint(() {return DateTime.Now.Year; }); int result task2.Result; Debug.WriteLine($result{result});3Task.Factory.StartNew() TaskFactory factory Task.Factory; factory.StartNew(() {this.DoSomething(张三); }); Taskint task2 factory.StartNewint(() {return DateTime.Now.Year; }); int result task2.Result; Debug.WriteLine($result{result});4new Task().RunSynchronously()同步方式上面三种异步方式 Task task new Task(() {this.DoSomething(张三); }); task.RunSynchronously(); Taskint task2 new Taskint(() {return DateTime.Now.Year; }); task2.RunSynchronously(); int result task2.Result; Debug.WriteLine($result{result});3、线程等待 1task.Wait() 等待task内部执行完毕才会往后直行卡主线程Task实例方法 task.Wait(1000);//等待1000毫秒后就往后直行不管有没有执行结束 task.Wait(TimeSpan.FromMilliseconds(1000));//等待1000毫秒后就往后直行不管有没有执行结束 Task task Task.Run(() {this.DoSomething(张三);}); task.Wait(); //task.Wait(TimeSpan.FromMilliseconds(1000)); //task.Wait(1000);2WaitAny 某一个任务执行结束后去触发一个动作卡主线程Task静态方法 数据有可能是来自于第三方接口缓存数据库查询的时候我们不确定开启几个线程同时查询只要一个返回了就返回界面 ListTask taskList new ListTask(); TaskFactory factory new TaskFactory(); taskList.Add(factory.StartNew(() { Debug.WriteLine(查询数据库); })); taskList.Add(factory.StartNew(() { Debug.WriteLine(查询缓存); })); taskList.Add(factory.StartNew(() { Debug.WriteLine(查询接口); })); Task.WaitAny(taskList.ToArray()); Debug.WriteLine($查到数据返回界面);3WaitAll 所有任务执行完成后去触发一个动作卡主线程Task静态方法 数据是来自于第三方接口缓存数据库查询的时候开启几个线程同时查询等所有数据全部查询出来一起返回界面 ListTask taskList new ListTask(); TaskFactory factory new TaskFactory(); taskList.Add(factory.StartNew(() { Debug.WriteLine(查询数据库); })); taskList.Add(factory.StartNew(() { Debug.WriteLine(查询缓存); })); taskList.Add(factory.StartNew(() { Debug.WriteLine(查询接口); })); Task.WaitAll(taskList.ToArray()); Debug.WriteLine($查到数据返回界面);4WhenAny 与下面ContinueWith配合执行,当传入的线程中任何一个线程执行完毕继续执行ContinueWith中的任务(属于开启新线程不卡主线程)Task静态方法 ListTask taskList new ListTask(); TaskFactory factory new TaskFactory(); taskList.Add(factory.StartNew(() { Debug.WriteLine(查询数据库); })); taskList.Add(factory.StartNew(() { Debug.WriteLine(查询缓存); })); taskList.Add(factory.StartNew(() { Debug.WriteLine(查询接口); })); Task.WhenAny(taskList.ToArray()).ContinueWith((n) { Debug.WriteLine($查到数据返回界面); });5WhenAll 当其中所有线程执行完成后新开启了一个线程执行继续执行新业务所以执行过程中不卡主线程Task静态方法 ListTask taskList new ListTask(); TaskFactory factory new TaskFactory(); taskList.Add(factory.StartNew(() { Debug.WriteLine(查询数据库); })); taskList.Add(factory.StartNew(() { Debug.WriteLine(查询缓存); })); taskList.Add(factory.StartNew(() { Debug.WriteLine(查询接口); })); Task.WhenAll(taskList.ToArray()).ContinueWith((n) { Debug.WriteLine($查到数据返回界面); });6ContinueWhenAny 某一个任务执行结束后去触发一个动作不卡主线程TaskFactory实例方法等价于WhenAnyContinueWith ListTask taskList new ListTask(); TaskFactory factory new TaskFactory(); taskList.Add(factory.StartNew(obj Coding(张三, 数据库设计), 张三)); taskList.Add(factory.StartNew(obj Coding(李四, 接口对接), 李四)); taskList.Add(factory.StartNew(obj Coding(王五, Webapi), 王五)); taskList.Add(factory.StartNew(obj Coding(赵六, 前端页面), 赵六)); factory.ContinueWhenAny(taskList.ToArray(), ts {Debug.WriteLine(${ts.AsyncState}同学开发完毕田七开始测试); });7ContinueWhenAll 所有任务执行完成后去触发一个动作不卡主线程TaskFactory实例方法等价于WhenAllContinueWith ListTask taskList new ListTask(); TaskFactory factory new TaskFactory(); taskList.Add(factory.StartNew(obj Coding(张三, 数据库设计), 张三)); taskList.Add(factory.StartNew(obj Coding(李四, 接口对接), 李四)); taskList.Add(factory.StartNew(obj Coding(王五, Webapi), 王五)); taskList.Add(factory.StartNew(obj Coding(赵六, 前端页面), 赵六)); factory.ContinueWhenAll(taskList.ToArray(), ts {Debug.WriteLine($所有人开发完毕我们一起庆祝一下吃个饭); });4、TaskCreationOptions枚举类详解 一个Task内部可以开启线程Task内部的线程可以理解为子线程Task为父线程创建Task实例的时候可以传入TaskCreationOptions枚举参数来影响线程的运行方式。 1None默认情况 父线程不会等待子线程执行结束才结束。 Task task new Task(() {Debug.WriteLine($task--start--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});Task task1 new Task(() {this.DoSomething(task1);});Task task2 new Task(() {this.DoSomething(task2);});task1.Start();task2.Start();Debug.WriteLine($task--end--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)}); }); task.Start(); task.Wait();2AttachedToParent 子线程附加到父线程父线程必须等待所有子线程执行结束才能结束相当于Task.WaitAll(task1, task2)。 Task task new Task(() {Debug.WriteLine($task--start--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});Task task1 new Task(() {this.DoSomething(task1);}, TaskCreationOptions.AttachedToParent);Task task2 new Task(() {this.DoSomething(task2);}, TaskCreationOptions.AttachedToParent);task1.Start();task2.Start();Debug.WriteLine($task--end--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)}); }); task.Start(); task.Wait();3DenyChildAttach 不允许子任务附加到父任务上反AttachedToParent和默认效果一样。 Task task new Task(() {Debug.WriteLine($task--start--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});Task task1 new Task(() {this.DoSomething(task1);}, TaskCreationOptions.AttachedToParent);Task task2 new Task(() {this.DoSomething(task2);}, TaskCreationOptions.AttachedToParent);task1.Start();task2.Start();Debug.WriteLine($task--end--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)}); }, TaskCreationOptions.DenyChildAttach); task.Start(); task.Wait();4PreferFairness 相对来说比较公平执行的先申请的线程优先执行。 Task task1 new Task(() {this.DoSomething(task1); }, TaskCreationOptions.PreferFairness); Task task2 new Task(() {this.DoSomething(task2); }, TaskCreationOptions.PreferFairness); task1.Start(); task2.Start();5LongRunning 事先知道是长时间执行的线程就加这个参数线程调度会优化。 6RunContinuationsAsynchronously 强制以异步方式执行添加到当前任务的延续。 7HideScheduler 防止环境计划程序被视为已创建任务的当前计划程序。 这意味着像 StartNew 或 ContinueWith 创建任务的执行操作将被视为 System.Threading.Tasks.TaskScheduler.Default当前计划程序。 5、TaskContinuationOptions枚举类详解 ContinueWith可以传入TaskContinuationOptions枚举类参数来影响线程的运行方式。 1None默认情况 任务顺序执行。 Task task1 new Task(() {this.DoSomething(task1); });Task task2 task1.ContinueWith(t {this.DoSomething(task2);});Task task3task2.ContinueWith(t {this.DoSomething(task3); });task1.Start();2LazyCancellation 取消该线程该线程的前一个线程和后一个线程顺序执行。 CancellationTokenSource source new CancellationTokenSource(); source.Cancel();Task task1 new Task(() {this.DoSomething(task1); });Task task2 task1.ContinueWith(t {this.DoSomething(task2); }, source.Token,TaskContinuationOptions.LazyCancellation, TaskScheduler.Current);Task task3 task2.ContinueWith(t {this.DoSomething(task3); });task1.Start();3ExecuteSynchronously 前后任务由同一个线程执行。 Task task1 new Task(() {this.DoSomething(task1); });Task task2 task1.ContinueWith(t {this.DoSomething(task2); },TaskContinuationOptions.ExecuteSynchronously);task1.Start();4NotOnRanToCompletion 延续任务必须在前面task非完成状态才能执行。 Task task1 new Task(() {this.DoSomething(task1);//异常了表示未执行完成task2能执行//不异常表示执行完成task2不能执行throw new Exception(手动制造异常表示不能执行完毕); });Task task2 task1.ContinueWith(t {this.DoSomething(task2); }, TaskContinuationOptions.NotOnRanToCompletion);task1.Start();5OnlyOnRanToCompletion 延续任务必须在前面task完成状态才能执行和NotOnRanToCompletion正好相反。 Task task1 new Task(() {this.DoSomething(task1);//异常了表示未执行完成task2不能执行//不异常表示执行完成task2能执行throw new Exception(手动制造异常表示不能执行完毕); });Task task2 task1.ContinueWith(t {this.DoSomething(task2); }, TaskContinuationOptions.OnlyOnRanToCompletion);task1.Start();6NotOnFaulted 延续任务必须在前面task完成状态才能执行效果和OnlyOnRanToCompletion差不多。 Task task1 new Task(() {this.DoSomething(task1);//throw new Exception(手动制造异常); });Task task2 task1.ContinueWith(t {this.DoSomething(task2); }, TaskContinuationOptions.NotOnFaulted);task1.Start();7OnlyOnFaulted 延续任务必须在前面task未完成状态才能执行效果和NotOnRanToCompletion差不多。 Task task1 new Task(() {this.DoSomething(task1);//throw new Exception(手动制造异常); });Task task2 task1.ContinueWith(t {this.DoSomething(task2); }, TaskContinuationOptions.OnlyOnFaulted);task1.Start();8OnlyOnCanceled 前面的任务未被取消才执行后面的任务。 CancellationTokenSource cts new CancellationTokenSource(); Task task1 new Task(() {this.DoSomething(task1);cts.Cancel(); });Task task2 task1.ContinueWith(t {this.DoSomething(task2); }, TaskContinuationOptions.OnlyOnCanceled);task1.Start();9NotOnCanceled 前面的任务被取消才执行后面的任务。 CancellationTokenSource cts new CancellationTokenSource(); Task task1 new Task(() {this.DoSomething(task1);cts.Cancel(); });Task task2 task1.ContinueWith(t {this.DoSomething(task2); }, TaskContinuationOptions.NotOnCanceled);task1.Start();10PreferFairness System.Threading.Tasks.TaskScheduler 以一种尽可能公平的方式安排任务这意味着较早安排的任务将更可能较早运行而较晚安排运行的任务将更可能较晚运行。 11LongRunning 指定某个任务将是运行时间长、粗粒度的操作。 它会向 System.Threading.Tasks.TaskScheduler 提示过度订阅可能是合理的。 12AttachedToParent 指定将任务附加到任务层次结构中的某个父级。 13DenyChildAttach 如果尝试附有子任务到创建的任务指定 System.InvalidOperationException 将被引发。 14HideScheduler 防止环境计划程序被视为已创建任务的当前计划程序。 这意味着像 StartNew 或 ContinueWith 创建任务的执行操作将被视为 System.Threading.Tasks.TaskScheduler.Default当前计划程序。 6、延迟执行 Task.Delay(),一般和ContinueWith配合使用执行的动作就是ContinueWith内部的委托委托的执行有可能是一个全新的线程也有可能是主线程。 //开启线程后线程等待3000毫秒后执行动作不卡主线程 Task.Delay(3000).ContinueWith(t {this.DoSomething(张三); });五、Task进阶 1、多线程捕获异常 1线程不等待捕捉不到异常 多线程中如果发生异常使用try-catch包裹捕捉不到异常异常还没发生主线程已经执行结束 //捕捉不到异常 try {Task task Task.Run(() {int i 0;int j 10;int k j / i; //尝试除以0会异常}); } catch (AggregateException aex) {foreach (var exception in aex.InnerExceptions){Debug.WriteLine($线程不等待异常{exception.Message});} }2线程不等待线程内部捕捉异常 多线程中如果要捕捉异常可以在线程内部try-catch可以捕捉到异常 //捕捉到异常 try {Task task Task.Run(() {try{int i 0;int j 10;int k j / i; //尝试除以0会异常}catch (Exception ex){Debug.WriteLine($线程内异常{ex.Message});}}); } catch (AggregateException aex) {foreach (var exception in aex.InnerExceptions){Debug.WriteLine($线程不等待异常{exception.Message});} }运行结果 线程内异常Attempted to divide by zero.3线程等待能够捕获异常 多线程中如果要捕捉异常需要设置主线程等待子线程执行结束可以捕捉到异常 多线程内部发生异常后抛出的异常类型是system.AggregateException //捕捉到异常 try { Task task Task.Run(() {int i 0;int j 10;int k j / i; //尝试除以0会异常 }); //线程等待 task.Wait(); } catch (AggregateException aex) { foreach (var exception in aex.InnerExceptions) {Debug.WriteLine($线程等待异常{exception.Message}); } }运行结果 线程等待异常Attempted to divide by zero.2、线程取消 线程取消是不能从外部取消的线程取消的实质还是通过变量去控制程序的运行和结束正常结束或者发生异常结束 1标准取消Cancel 实例化一个CancellationTokenSource 包含了一个IsCancellationRequested属性属性值默认为false 包含了一个Cancel方法Cancel方法如果被执行IsCancellationRequested属性值马上更新成true 线程内部判断IsCancellationRequested值结束线程 包含了一个Token属性可以Register注册一个委托创建Task的时候传入线程结束后调用 //初始化一个CancellationTokenSource实例 CancellationTokenSource source new CancellationTokenSource();//注册一个线程取消后执行的逻辑 source.Token.Register(() {this.DoSomething(张三); });Task.Run(() {while (!source.IsCancellationRequested){this.DoSomething(李四, 500);} }, source.Token);//Thread.Sleep阻塞主线程 Thread.Sleep(2000); //Cancel方法更新IsCancellationRequested的值 source.Cancel();运行结果 DoSomething--Start--李四--04--19:04:32.592 DoSomething--End--李四--04--19:04:33.094 DoSomething--Start--李四--04--19:04:33.096 DoSomething--End--李四--04--19:04:33.597 DoSomething--Start--李四--04--19:04:33.598 DoSomething--End--李四--04--19:04:34.100 DoSomething--Start--李四--04--19:04:34.101 DoSomething--Start--张三--01--19:04:34.587 DoSomething--End--李四--04--19:04:34.604 DoSomething--End--张三--01--19:04:36.5882延迟取消CancelAfter CancellationTokenSource source new CancellationTokenSource(); //注册一个线程取消后执行的逻辑 source.Token.Register(() { this.DoSomething(张三); });Task.Run(() {while (!source.IsCancellationRequested){this.DoSomething(李四, 500);} }, source.Token);//2s后自动取消等待取消期间不阻塞主线程 source.CancelAfter(new TimeSpan(0, 0, 0, 2));运行结果 DoSomething--Start--李四--04--18:49:48.601 DoSomething--End--李四--04--18:49:49.102 DoSomething--Start--李四--04--18:49:49.104 DoSomething--End--李四--04--18:49:49.606 DoSomething--Start--李四--04--18:49:49.607 DoSomething--End--李四--04--18:49:50.109 DoSomething--Start--李四--04--18:49:50.110 DoSomething--Start--张三--05--18:49:50.599 DoSomething--End--李四--04--18:49:50.612 DoSomething--End--张三--05--18:49:52.6023CancellationTokenSource构造函数取消 //2s后自动取消 CancellationTokenSource source new CancellationTokenSource(2000); //注册一个线程取消后执行的逻辑 source.Token.Register(() {this.DoSomething(张三); });Task.Run(() {while (!source.IsCancellationRequested){this.DoSomething(李四, 500);} }, source.Token);运行结果 DoSomething--Start--李四--04--19:17:11.726 DoSomething--End--李四--04--19:17:12.228 DoSomething--Start--李四--04--19:17:12.229 DoSomething--End--李四--04--19:17:12.731 DoSomething--Start--李四--04--19:17:12.733 DoSomething--End--李四--04--19:17:13.235 DoSomething--Start--李四--04--19:17:13.237 DoSomething--Start--张三--05--19:17:13.720 DoSomething--End--李四--04--19:17:13.741 DoSomething--End--张三--05--19:17:15.7224CreateLinkedTokenSource组合取消 利用CreateLinkedTokenSource构建CancellationTokenSource的组合体其中任何一个体取消则组合体就取消 CancellationTokenSource source1 new CancellationTokenSource(); CancellationTokenSource source2 new CancellationTokenSource(); CancellationTokenSource source3 new CancellationTokenSource(); var combineSource CancellationTokenSource.CreateLinkedTokenSource(source1.Token, source2.Token,source3.Token); source2.Cancel(); Debug.WriteLine($source1.IsCancellationRequested{source1.IsCancellationRequested}); Debug.WriteLine($source2.IsCancellationRequested{source2.IsCancellationRequested}); Debug.WriteLine($source3.IsCancellationRequested{source3.IsCancellationRequested}); Debug.WriteLine($combineSource.IsCancellationRequested{combineSource.IsCancellationRequested});运行结果 source1.IsCancellationRequestedFalse source2.IsCancellationRequestedTrue source3.IsCancellationRequestedFalse combineSource.IsCancellationRequestedTrue5CancellationToken类监控取消 取消之后调用ThrowIfCancellationRequested就会抛异常不取消不会抛异常 CancellationTokenSource source1 new CancellationTokenSource(); CancellationTokenSource source2 new CancellationTokenSource(); CancellationTokenSource source3 new CancellationTokenSource(); //等价于上面那句话 var combineSource CancellationTokenSource.CreateLinkedTokenSource(source1.Token, source2.Token,source3.Token); source2.Cancel(); try {combineSource.Token.ThrowIfCancellationRequested(); } catch (Exception) {Debug.WriteLine(报错了); } Debug.WriteLine($source1.IsCancellationRequested{source1.IsCancellationRequested}); Debug.WriteLine($source2.IsCancellationRequested{source2.IsCancellationRequested}); Debug.WriteLine($source3.IsCancellationRequested{source3.IsCancellationRequested}); Debug.WriteLine($combineSource.IsCancellationRequested{combineSource.IsCancellationRequested});运行结果 报错了 source1.IsCancellationRequestedFalse source2.IsCancellationRequestedTrue source3.IsCancellationRequestedFalse combineSource.IsCancellationRequestedTrue3、线程返回值 1线程开启类的返回值 Taskstring task1 Task.Run(() {this.DoSomething(张三);return ok; }); //要读取返回值会阻塞主线程 Debug.WriteLine($我是主线程我要读取子线程task1的返回值为{task1.Result}--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});运行结果 DoSomething--Start--张三--06--19:45:49.765 DoSomething--End--张三--06--19:45:51.773 我是主线程我要读取子线程task1的返回值为ok--01--19:45:51.7742线程延续类的返回值 Taskint task1 Task.Run(() {this.DoSomething(task1);return 2; });var task2 task1.ContinueWith((t) {this.DoSomething(task2);//这里的t代表 task1var num t.Result 2;return num.ToString(); });Debug.WriteLine($我是主线程我要读取子线程task1的返回值为{task1.Result}--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)}); Debug.WriteLine($我是主线程我要读取子线程task2的返回值为{task2.Result}--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});运行结果 DoSomething--Start--task1--05--19:52:11.346 DoSomething--End--task1--05--19:52:13.349 我是主线程我要读取子线程task1的返回值为2--01--19:52:13.357 DoSomething--Start--task2--04--19:52:13.359 DoSomething--End--task2--04--19:52:15.361 我是主线程我要读取子线程task2的返回值为4--01--19:52:15.3633线程条件延续类 Taskint task1 Task.Run(() {this.DoSomething(task1);return 2; }); Taskint task2 Task.Run(() {this.DoSomething(task2);return 4; });var task Task.WhenAny(new Taskint[2] { task1, task2 });//下面的值可能是1也可能是2 Debug.WriteLine($我是主线程我要读取子线程的返回值为{task.Result.Result}--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});运行结果 DoSomething--Start--task2--04--09:05:57.574 DoSomething--Start--task1--05--09:05:57.575 DoSomething--End--task1--05--09:05:59.583 DoSomething--End--task2--04--09:05:59.583 我是主线程我要读取子线程的返回值为2--01--09:05:59.5874、线程安全 线程安全一段业务逻辑单线程执行和多线程执行后的结果如果完全一致是线程安全的否则就是线程不安全的 1线程安全产生的原因 线程的开启需要时间线程开启不阻塞主线程的执行循环10000次的时间不足够开启10000个线程的时间就会出现数据不足10000的现象出现 单线程执行这段循环完毕以后intlist中有10000条数据 Listint intlist new Listint(); for (int i 0; i 10000; i) {intlist.Add(i); } Debug.WriteLine($intlist中有{intlist.Count}条数据);多线程执行这段循环完毕以后intlist中有多少条数据第一次执行9937第二次执行9976 Listint intlist new Listint(); ListTask tasklist new ListTask(); for (int i 0; i 10000; i) {Task.Run(() {intlist.Add(i);}); } Task.WaitAll(tasklist.ToArray()); Debug.WriteLine($intlist中有{intlist.Count}条数据);2加锁解决线程安全问题 锁的本质是独占引用加锁是反多线程的可以解决线程安全问题但是不推荐大家使用加锁会影响性能锁的标准写法 private readonly static object obj_Lock new object(); 锁对象不要去锁String锁This Listint intlist new Listint(); ListTask tasklist new ListTask(); for (int i 0; i 10000; i) {Task.Run(() {lock (obj_Lock){intlist.Add(i);}}); } Task.WaitAll(tasklist.ToArray()); Debug.WriteLine($intlist中有{intlist.Count}条数据);3分块执行解决线程安全问题 把执行的任务切割然后分别开启一个线程执行每一个线程内部执行的动作是单线程线程安全等待所有线程执行结束以后再做一个统一汇总 Listint intlist new Listint(); Listint intlist2 new Listint(); Listint intlist3 new Listint(); int Num1 3000; int Num2 6000; int Num3 10000; ListTask taskList new ListTask(); taskList.Add(Task.Run(() {for (int i 0; i Num1; i){intlist.Add(i);} })); taskList.Add(Task.Run(() {for (int i Num1; i Num2; i){intlist2.Add(i);} }));taskList.Add(Task.Run(() {for (int i Num2; i Num3; i){intlist3.Add(i);} })); Task.WaitAll(taskList.ToArray()); intlist.AddRange(intlist2); intlist.AddRange(intlist3); Debug.WriteLine($intlist中有{intlist.Count}条数据);4使用线程安全对象 BlockingCollectionint blockinglist new BlockingCollectionint(); ConcurrentBagint conocurrentbag new ConcurrentBagint(); ConcurrentDictionarystring, int concurrentDictionary new ConcurrentDictionarystring, int(); ConcurrentQueueint concurrentQueue new ConcurrentQueueint(); ConcurrentStackint concurrentStack new ConcurrentStackint();5、解决中间变量问题 1单线程执行 for (int i 0; i 5; i) {Debug.WriteLine($ThreadID{Thread.CurrentThread.ManagedThreadId.ToString(00)}_i{i}); }2多线程执行问题 Task开启线程的时候延迟开启在循环的时候不会阻塞主线程循环很快线程执行业务逻辑的时候循环已经结束了i已经变成5了所以打出来的都是5 for (int i 0; i 5; i) {Task.Run(() {Debug.WriteLine($ThreadID{Thread.CurrentThread.ManagedThreadId.ToString(00)}_i{i});}); }3多线程执行中间变量 可以另外定义个变量在每次循环的时候赋值循环多少次就会有多少个k每个线程使用的是每一次循环内部的k for (int i 0; i 5; i) {int k i;Task.Run(() {Debug.WriteLine($ThreadID{Thread.CurrentThread.ManagedThreadId.ToString(00)}_k{ k});}); }六、Parallel 1、Parallel特点 可以传入多个委托多个委托中的内容是会开启线程来执行执行的线程可能是新的线程也可能是主线程会阻塞主线程相当于是主线程等待子线程执行结束 Parallel.Invoke(() this.DoSomething(张三),() this.DoSomething(李四),() this.DoSomething(王五),() this.DoSomething(赵六));运行结果 DoSomething--Start--赵六--04--11:01:20.666 DoSomething--Start--王五--06--11:01:20.666 DoSomething--Start--张三--01--11:01:20.666 DoSomething--Start--李四--05--11:01:20.666 DoSomething--End--赵六--04--11:01:22.668 DoSomething--End--王五--06--11:01:22.696 DoSomething--End--张三--01--11:01:22.702 DoSomething--End--李四--05--11:01:22.703可以传入options.MaxDegreeOfParallelism来限制开启的线程数量可以做到不影响线程池的线程数量又能控制当前执行所用的线程数量 ParallelOptions options new ParallelOptions(); options.MaxDegreeOfParallelism 2; Parallel.Invoke(options,() this.DoSomething(张三),() this.DoSomething(李四),() this.DoSomething(王五),() this.DoSomething(赵六));运行结果 DoSomething--Start--张三--01--11:03:20.700 DoSomething--Start--李四--09--11:03:20.702 DoSomething--End--张三--01--11:03:22.704 DoSomething--Start--王五--01--11:03:22.706 DoSomething--End--李四--09--11:03:22.710 DoSomething--Start--赵六--09--11:03:22.711 DoSomething--End--王五--01--11:03:24.707 DoSomething--End--赵六--09--11:03:24.714把Parallel包在一个Task里面实现不卡主线程 Task.Run(() {Parallel.Invoke(() this.DoSomething(张三),() this.DoSomething(李四),() this.DoSomething(王五),() this.DoSomething(赵六)); });2、Parallel.For 实现循环开启线程执行动作可以获取索引可以控制开启的线程数量 ParallelOptions options new ParallelOptions(); options.MaxDegreeOfParallelism 3; Parallel.For(0, 10, options, index {Debug.WriteLine($index{ index} 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString(00)}); });运行结果 index3 线程ID: 05 index0 线程ID: 01 index6 线程ID: 04 index1 线程ID: 01 index7 线程ID: 04 index8 线程ID: 04 index4 线程ID: 05 index5 线程ID: 05 index2 线程ID: 01 index9 线程ID: 043、Parallel.ForEach 实现循环遍历数组开启线程执行动作可以获取数组值可以控制开启的线程数量 Listint intlist new Listint() { 1, 2, 3, 5, 7, 11, 13, 17 }; ParallelOptions options new ParallelOptions(); options.MaxDegreeOfParallelism 3; Parallel.ForEach(intlist, options, s {Debug.WriteLine($index{ s} 线程ID: {Thread.CurrentThread.ManagedThreadId.ToString(00)}); });运行结果 index1 线程ID: 01 index7 线程ID: 08 index2 线程ID: 01 index11 线程ID: 08 index5 线程ID: 01 index13 线程ID: 08 index3 线程ID: 09 index17 线程ID: 08七、await/async 1、async/await是什么 1C#5 (.NET4.5) 引入的语法糖 2C#7.1Main入口也可以 3C#8.0可以使用异步流awaitforeach可以释放对象await using 2、await/async用法 1无返回值有Async无Await async是用来修饰方法如果单独出现方法会警告不会报错和普通的多线程方法没有什么区别不存在线程等待的问题 /// summary /// 无返回值有Async无Await /// /summary private async void NoReturnAsyncNoAwait() {Console.WriteLine($NoReturnAsyncNoAwait Start ThreadId{Thread.CurrentThread.ManagedThreadId}--{DateTime.Now.ToString(HH:mm:ss.fff)});Thread.Sleep(3000);Console.WriteLine($NoReturnAsyncNoAwait End ThreadId{Thread.CurrentThread.ManagedThreadId}--{DateTime.Now.ToString(HH:mm:ss.fff)}); }2无返回值有Async有Await await在方法体内部只能放在async修饰的方法内必须放在task前面主线程到await这里就返回了执行主线程任务task中的任务执行完毕以后继续执行await后面的后续内容有可能是子线程也有可能是其他线程甚至有可能是主线程来执行类似ContinueWith回调await后面的后续内容 /// summary /// 无返回值有Async有Await /// /summary private async void NoReturnAsyncAwait() {Debug.WriteLine($NoReturnAsyncAwait--Start--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});Task task Task.Run(() {Thread.Sleep(3000);Debug.WriteLine($Task.Run--NoReturnAsyncAwait--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});});//主线程到await这里就返回了执行主线程任务//task中的任务执行完毕以后继续执行await后面的后续内容有可能是子线程也有可能是其他线程甚至有可能是主线程来执行await task;Debug.WriteLine($NoReturnAsyncAwait--End--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});//类似ContinueWith回调await后面的后续内容//task.ContinueWith(t //{// Debug.WriteLine($NoReturnAsyncAwait--End--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});//}); }运行结果 btnAwaitAsync_Click--Start--01--13:40:10.935 NoReturnAsyncAwait--Start--01--13:40:10.954 btnAwaitAsync_Click--End--01--13:40:10.987 Task.Run--NoReturnAsyncAwait--05--13:40:13.992 NoReturnAsyncAwait--End--01--13:40:14.0343返回Task有Async有Await async Task async void,Task和Task能够使用await, Task.WhenAny, Task.WhenAll等方式组合使用。Async Void 不行 方法 /// summary /// 无返回值返回Task有Async有Await /// /summary /// returns/returns private async Task NoReturnTaskAsyncAwait() {Debug.WriteLine($NoReturnTaskAsyncAwait--Start--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});Task task Task.Run(() {Thread.Sleep(3000);Debug.WriteLine($Task.Run--NoReturnTaskAsyncAwait--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});});await task;Debug.WriteLine($NoReturnTaskAsyncAwait--End--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)}); }方法调用 Task task NoReturnTaskAsyncAwait(); Task.WhenAny(task).ContinueWith((a) {this.DoSomething(张三); });运行结果 btnAwaitAsync_Click--Start--01--14:05:33.611 NoReturnTaskAsyncAwait--Start--01--14:05:33.624 btnAwaitAsync_Click--End--01--14:05:33.674 Task.Run--NoReturnTaskAsyncAwait--04--14:05:36.686 NoReturnTaskAsyncAwait--End--01--14:05:36.702 DoSomething--Start--张三--06--14:05:36.718 DoSomething--End--张三--06--14:05:38.7334返回Task int有Async有Await 要使用返回值就一定要等子线程计算完毕 方法 /// summary /// 返回Taskint有Async有Await /// /summary /// returns/returns private Taskint ReturnTaskIntAsyncAwait() {Debug.WriteLine($ReturnTaskIntAsyncAwait--Start--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});TaskFactory taskFactory new TaskFactory();Taskint iResult Task.Run(() {Thread.Sleep(3000);Debug.WriteLine($Task.Run--ReturnTaskIntAsyncAwait--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});return 123;});Debug.WriteLine($ReturnTaskIntAsyncAwait--End--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});return iResult; }方法调用 Taskint result ReturnTaskIntAsyncAwait(); Debug.WriteLine($ReturnTaskIntAsyncAwait--Result--{result.Result}--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});运行结果 btnAwaitAsync_Click--Start--01--14:09:03.839 ReturnTaskIntAsyncAwait--Start--1--14:09:03.851 ReturnTaskIntAsyncAwait--End--1--14:09:03.874 Task.Run--ReturnTaskIntAsyncAwait--5--14:09:06.903 ReturnTaskIntAsyncAwait--Result--123--01--14:09:06.911 btnAwaitAsync_Click--End--01--14:09:06.9175返回Task实现多个任务顺序执行不阻塞 /// summary /// 返回Task实现多个任务顺序执行不阻塞 /// /summary /// returnsasync 就只返回long/returns private async Task ReturnTaskAsyncAwaits() {Debug.WriteLine($ReturnTaskAsyncAwaits--Start--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});await Task.Run(() {this.DoSomething(task1);});await Task.Run(() {this.DoSomething(task2);}); await Task.Run(() {this.DoSomething(task3);});Debug.WriteLine($ReturnTaskAsyncAwaits--Start--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)}); }运行结果 btnAwaitAsync_Click--Start--01--14:38:07.608 ReturnTaskAsyncAwaits--Start--01--14:38:07.627 DoSomething--Start--task1--04--14:38:07.703 btnAwaitAsync_Click--End--01--14:38:07.709 DoSomething--End--task1--04--14:38:09.719 DoSomething--Start--task2--04--14:38:09.753 DoSomething--End--task2--04--14:38:11.773 DoSomething--Start--task3--05--14:38:11.781 DoSomething--End--task3--05--14:38:13.799 ReturnTaskAsyncAwaits--Start--01--14:38:13.8066在Winform中存在特殊处理 更改控件的值必须是(UI线程)主线程去执行这跟Winform设计有关系在Winform中await后面的内容都会让主线程来执行 计算方法 private async Tasklong CalculationAsync(long total) {var task await Task.Run(() {Debug.WriteLine($This is CalculationAsync Start,ThreadId{Thread.CurrentThread.ManagedThreadId});long lResult 0;for (int i 0; i total; i){lResult i;}Debug.WriteLine($This is CalculationAsync End,ThreadId{Thread.CurrentThread.ManagedThreadId});return lResult;});return task; //这句话必须由主线程来执行线程在同一时刻只能做一件事儿 }方法调用 /// summary /// 在Winform中存在特殊处理 /// /summary private async Task TextAsyncResultChange() {Debug.WriteLine($TextAsyncResultChange--End--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)});long lResult await this.CalculationAsync(1_000_000);//更改控件的值必须是(UI线程)主线程去执行这跟Winform设计有关系在Winform中await后面的内容都会让主线程来执行this.textAsyncResult.Text lResult.ToString();Debug.WriteLine($TextAsyncResultChange--End--{Thread.CurrentThread.ManagedThreadId.ToString(00)}--{DateTime.Now.ToString(HH:mm:ss.fff)}); }运行结果 btnAwaitAsync_Click--Start--01--15:31:16.624 TextAsyncResultChange--End--01--15:31:16.636 This is CalculationAsync Start,ThreadId6 btnAwaitAsync_Click--End--01--15:31:16.735 This is CalculationAsync End,ThreadId6 TextAsyncResultChange--End--01--15:31:16.7873、async/await好处 1既要有顺序又要不阻塞降低了编程难度 2以同步编程的方式来写异步 4、async/await原理 如果给方法加上Async在底层会生成一个状态机一个对象在不同的状态可以执行的不同的行为 1实例化状态机 2把状态机实例交给一个build去执行 3整理线程的上下文 4stateMachine.MoveNext(); 5MoveNext如何执行先获取一个状态继续往后执行 6如果有异常抛出异常把状态重置为-2 7如果没有异常把状态重置重置为-2 8SetResult();把结果包裹成一个Task 5、async/await优势场景 计算机的计算任务可以分成两类计算密集型任务和IO密集型任务,async/await和Task相比降低了线程使用数量性能相当不能提高计算速度优势就是在同等硬件基础上系统的吞吐率更高对计算密集型任务没有优势IO密集型计算有优势常见的IO密集型任务有 1Web请求Api请求 2文件读写 3数据库请求 4跟第三方交互的非托管资源 八、多线程双色球项目 需求双色球投注号码由6个红色球号码和1个蓝色球号码组成红色球号码从01–33中选择不重复蓝色球号码从01–16中选择 界面 代码实现 using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms;namespace MyThread {public partial class FrmSSQ : Form{/// summary /// 多线程双色球项目/// 需求双色球投注号码由6个红色球号码和1个蓝色球号码组成红色球号码从01--33中选择不重复蓝色球号码从01--16中选择/// /summarypublic FrmSSQ(){InitializeComponent();}#region Data /// summary/// 红球集合 其实可以写入配置文件/// /summaryprivate string[] RedNums {01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33};/// summary/// 蓝球集合 球号码可以放在配置文件/// /summaryprivate string[] BlueNums {01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16};private bool IsGoOn true;private ListTask taskList new ListTask();private static object object_Lock new object();#endregion/// summary/// 点击开始/// /summary/// param namesender/param/// param namee/paramprivate void btnStart_Click(object sender, EventArgs e){#region 初始化动作this.btnStart.Text 运行ing;this.lblBlue.Text 00;this.lblRed1.Text 00;this.lblRed2.Text 00;this.lblRed3.Text 00;this.lblRed4.Text 00;this.lblRed5.Text 00;this.lblRed6.Text 00;#endregionthis.btnStart.Enabled false;this.btnStop.Enabled true;taskList.Clear();Thread.Sleep(1000);//1.读取界面上有多少个球号码//2.循环球号码个数每循环一次开启一个线程foreach (var control in this.gboSSQ.Controls){if (control is Label){Label label (Label)control; //只对lable处理if (label.Name.Contains(Blue)) //蓝色球{taskList.Add( Task.Run(() //开启一个线程{//目标需要让这个球不断的跳动变化//1.获取号码值---号码的区间 建议写在配置文件就应该在BlueNums数组中找数据//2.赋值//3.循环 while (IsGoOn){//数组找数据通过索引先确定索引找到1-15的随机值作为索引值然后去数组中取出数据//new Random().Next(0, 16);int index new RandomHelper().GetRandomNumberDelay(0, 16);string blueNum this.BlueNums[index];//this.lblBlue.Text blueNum;//不允许---需要让主线程来帮助完成这件事儿this.Invoke(new Action(() //子线程委托出去让主线程帮助完成一件事儿{lblBlue.Text blueNum;}));}}));}else //红色球{taskList.Add( Task.Run(() { while (IsGoOn){ int index new RandomHelper().GetRandomNumberDelay(0, 33);string redNum this.RedNums[index]; lock (object_Lock){var currentNumberlist GetCurrentNumberList();if (!currentNumberlist.Contains(redNum)){this.Invoke(new Action(() {label.Text redNum;}));} } //问题号码重复如何解决//1.赋值的时候判断是否有重复//2.在赋值的时候进行判断如果界面上没有重复数据就赋值否则就重新生成index重新获取值//3.锁}})); } }}}/// summary/// 获取界面上所有红色球的球号码/// /summary/// returns/returnspublic Liststring GetCurrentNumberList(){Liststring numberlist new Liststring();foreach (var control in this.gboSSQ.Controls){if (control is Label){Label label (Label)control; //只对lable处理if (label.Name.Contains(Red)){numberlist.Add(label.Text);} }}//写代码测试if (numberlist.Count(ss00)0 numberlist.Distinct().Count()6){Console.WriteLine(********************有重复************************);foreach (var num in numberlist){Console.WriteLine(num);}}return numberlist;}/// summary/// 点击结束 /// /summary/// param namesender/param/// param namee/paramprivate void btnStop_Click(object sender, EventArgs e){Task.Run(() {this.IsGoOn false;//还要等待所有线程都执行结束才能输出结果 Task.WaitAll(taskList.ToArray()); //死锁是主线程和子线程相互等待引起的 this.Invoke(new Action(() {this.btnStart.Text Start;this.btnStart.Enabled true;this.btnStop.Enabled false;this.IsGoOn true;}));ShowResult();}); }/// summary/// 弹框提示数据/// /summaryprivate void ShowResult(){MessageBox.Show(string.Format(本期双色球结果为{0} {1} {2} {3} {4} {5} 蓝球{6}, this.lblRed1.Text, this.lblRed2.Text, this.lblRed3.Text, this.lblRed4.Text, this.lblRed5.Text, this.lblRed6.Text, this.lblBlue.Text));}} }运行效果
http://www.tj-hxxt.cn/news/137584.html

相关文章:

  • 上每网站建设如何帮人做网站
  • 海西州电子商务网站建设北京优化词网站
  • wordpress小工具开发甘肃省seo关键词优化
  • 普洱市交通建设集团官方网站wordpress建站双语
  • 中国空间站纪念币wordpress主题xiu
  • 网站漂浮广告效果wordpress 可以干嘛
  • 购物网站建设个人总结网上购物哪个平台质量好又便宜
  • 专业的国内网站建设公司手机app模板免费下载
  • 可以更改上传到网站的文件内容吗太原建站模板厂家
  • 境外 网站网站手机制作小程序软件的app
  • 阳江网站建设推广wordpress空页面模板
  • 包装设计网站官网seo短视频新地址在哪里?
  • 网站flash导入页网络推广讲师培训
  • 网站导航二级菜单怎么做出来的企业邮箱域名查询
  • 合肥专业做网站的网站和管理系统的区别
  • 微博营销网站源码怎样创建一个平台
  • 曲靖做网站价格上海市网站
  • 如何免费建购物网站有哪些可以做包装袋的网站
  • 麦包包的网站建设分站式二手车网站源码
  • 东方市住房和城乡建设局网站有什么学做木工的网站吗
  • 做网站前必须设计原型吗湛江网站建设哪家好
  • 网站前期准备工作山西省最新干部调整
  • 服装代销的网站源码建一个网站的价格
  • 南昌做网站设计产品推广方案要包含哪些内容
  • 一流小说网站模板做网站卖假名牌违法吗
  • 毕业设计网站设计步骤中国机械加工信息网
  • 外国人做旅游攻略网站优化好的网站
  • 如何做电影下载网站网上运营培训班
  • 青岛建站c 网站开发 vs2012
  • 惠州seo博客seo快速优化排名