使用Task的一些知识优化了一下同事的多线程协作取消的一串代码

使用Task的一些知识优化了一下同事的多线程协作取消的一串代码,第1张

概述最近在看一个同事的代码,代码的本意是在main方法中开启10个线程,用这10个线程来处理一批业务逻辑,在某一时刻当你命令console退出的时候,这个时候不是立即让console退出,而是需要等待10个线程把检测状态之后的业务逻辑执行完之后再退出,这样做是有道理的,如果强行退出会有可能造成子线程的业务数据损坏,没毛病吧,业务逻辑大概就是这样。 一:现实场景由于真实场景的代码比较复杂和繁琐,为了方便演示,我将同事所写的代码抽象一下,类似下面这样,看好了咯~~~1 class Program2 {3 private static int workThreadNums = 0;45 private static bool isStop = false;67 static void Main(string[] args)8 {9 var tasks = new Task[10];101112 for (int i = 0; i < 10; i++)13 {14 tasks[i] = Task.Factory.StartNew((obj) =>15 {16 Run();17 }, i);18 }1920 //是否退出21 string input = Console.ReadLine();2223 while ("Y".Equals(input, StringComparison.OrdinalIgnoreCase))24 {25 break;26 }2728 isStop = true;2930 while (workThreadNums != 0)31 {32 Console.WriteLine("正在等待线程结束,当前还在运行线程有:{0}", workThreadNums);3334 Thread.Sleep(10);35 }36 Console.WriteLine("准备退出了。。。");37 Console.Read();38 Environment.Exit(0);39 }4041 static void Run()42 {43 try44 {45 workThreadNums++;4647 while (true)48 {49 if (isStop) break;5051 Thread.Sleep(1000);5253 //执行业务逻辑54 Console.WriteLine("我是线程:{0},正在执行业务逻辑", Thread.CurrentThread.ManagedThreadId);55 }56 }57 finally58 {59 workThreadNums--;60 }61 }62 }       其实扫一下上面的代码应该就知道是用来干嘛的,业务逻辑没毛病,基本可以实现刚才的业务场景,在console退出的时候可以完全确保10个线程都把自己的业务逻辑处理完毕了。不过从美观角度上来看,这种代码就太low了。。。一点档次都没有,比如存在下面两点问题: 第一点:局部变量太多,又是isStop又是workThreaNums,导致业务逻辑Run方法中掺杂了很多的非业务逻辑,可读性和维护性都比较low。第二点:main函数在退出的时候用while检测workThreadNums是否为“0”,貌似没问题,但仔细想想这段代码有必要吗? 接下来我把代码跑一下,可以看到这个while检测到了在退出时的workThredNums的中间状态“7”,有点意思吧~~~ 二:代码优化  那上面这段代码怎么优化呢?如何踢掉业务逻辑方法中的非业务代码呢?当然应该从业务逻辑上考虑一下了,其实这个问题的核心就是两点: 1. 如何实现多线程中的协作取消?2. 如何实现多线程整体执行完毕通知主线程? 这种场景优化千万不要受到前人写的代码所影响,最好忘掉就更好了,不然你会下意识的受到什么workthreadnums,isstop这些变量的左右,不说废话了,如果你对task并发模型很熟悉的话,你的优化方案很快就会出来的。。。 1. 协作取消:    直接用一个bool变量来判断子线程是否退出的办法其实是很没有档次的,在net 4.0中有一个类(CancellationTokenSource)专门来解决使用bool变量来判断的这种很low的场景,而且比bool变量具有更强大的功能,这个会在以后的文章中跟大家去讲。 2. 多线程整体执行完毕通知主线程    目前我们看到的方式是主线程通过轮询workthreadnums这种没有档次的方式去做的,其实这种方式本质上就是任务串行,而如果你明白task的话,你就知道有很多的手段是执行任务串行的,比如什么ContinueWith,WhenAll,WhenAny等等方式,所以你只需要将一组task串联到WhenAll之后就可以了。好了,上面就是我的解决思路,接下来看一下代码吧:1 class Program2 {3 static void Main(string[] args)4 {5 CancellationTokenSource source = new CancellationTokenSource();67 var tasks = new Task[10];89 for (int i = 0; i < 10; i++)10 {11 tasks[i] = Task.Factory.StartNew((m) =>12 {13 Run(source.Token);14 }, i);15 }1617 Task.WhenAll(tasks).ContinueWith((t) =>18 {19 Console.WriteLine("准备退出了。。。");20 Console.Read();21 Environment.Exit(0);22 });2324 string input = Console.ReadLine();25 while ("Y".Equals(input, StringComparison.OrdinalIgnoreCase))26 {27 source.Cancel();28 }2930 Console.Read();31 }3233 static void Run(CancellationToken token)34 {35 while (true)36 {37 if (token.IsCancellationRequested) break;3839 Thread.Sleep(1000);4041 //执行业务逻辑42 Console.WriteLine("我是线程:{0},正在执行业务逻辑", Thread.CurrentThread.ManagedThreadId);43 }44 }45 }        单从代码量上面看就缩减了17行代码,而且业务逻辑也非常的简单明了,然后再看业务逻辑方法Run,其实你根本就不需要所谓的workThreadNums++,--的 *** 作,而且多线程下不用锁的话,还容易出现竞态的问题,解决方案就是使用WhenAll等待一组Tasks完成任务,之后再串行要退出的Task任务,是不是很完美,而协作取消的话,只需将取消的token传递给业务逻辑方法,当主线程执行source.Cancel()方法取消的时候,子线程就会通过IsCancellationRequested感知到主线程做了取消 *** 作。 好了,就说这么多吧,还是那句话,”因为我们视野的不开阔,导致缺乏解决问题的手段“,所以古话说得好,磨刀不误砍柴工。。。

  最近在看一个同事的代码,代码的本意是在main方法中开启10个线程,用这10个线程来处理一批业务逻辑,在某一时刻当你命令console退出的时候,这个

时候不是立即让console退出,而是需要等待10个线程把检测状态之后的业务逻辑执行完之后再退出,这样做是有道理的,如果强行退出会有可能造成子线程的业

务数据损坏,没毛病吧,业务逻辑大概就是这样。

由于真实场景的代码比较复杂和繁琐,为了方便演示,我将同事所写的代码抽象一下,类似下面这样,看好了咯~~~

workThreadNums = isstop = Main( tasks = Task[ ( i = ; i < ; i++ tasks[i] = Task.Factory.StartNew((obj) => input = ( isstop = (workThreadNums != Console.Writeline( Thread.Sleep( Console.Writeline( Environment.Exit( workThreadNums++ ( (isstop) Thread.Sleep( Console.Writeline( workThreadNums-- }

      其实扫一下上面的代码应该就知道是用来干嘛的,业务逻辑没毛病,基本可以实现刚才的业务场景,在console退出的时候可以完全确保10个线程都把自己的业

务逻辑处理完毕了。不过从美观角度上来看,这种代码就太low了。。。一点档次都没有,比如存在下面两点问题:

第一点:局部变量太多,又是isstop又是workThreaNums,导致业务逻辑Run方法中掺杂了很多的非业务逻辑,可读性和维护性都比较low。

第二点:main函数在退出的时候用while检测workThreadNums是否为“0”,貌似没问题,但仔细想想这段代码有必要吗?

接下来我把代码跑一下,可以看到这个while检测到了在退出时的workThrednums的中间状态“7”,有点意思吧~~~

  那上面这段代码怎么优化呢?如何踢掉业务逻辑方法中的非业务代码呢?当然应该从业务逻辑上考虑一下了,其实这个问题的核心就是两点:

这种场景优化千万不要受到前人写的代码所影响,最好忘掉就更好了,不然你会下意识的受到什么workthreadnums,isstop这些变量的左右,不说废话了,如

果你对task并发模型很熟悉的话,你的优化方案很快就会出来的。。。

    直接用一个bool变量来判断子线程是否退出的办法其实是很没有档次的,在net 4.0中有一个类(CancellationTokenSource)专门来解决使用bool变量来判

断的这种很low的场景,而且比bool变量具有更强大的功能,这个会在以后的文章中跟大家去讲。

    目前我们看到的方式是主线程通过轮询workthreadnums这种没有档次的方式去做的,其实这种方式本质上就是任务串行,而如果你明白task的话,你就知道

有很多的手段是执行任务串行的,比如什么ContinueWith,WhenAll,WhenAny等等方式,所以你只需要将一组task串联到WhenAll之后就可以了。好了,上

面就是我的解决思路,接下来看一下代码吧:

Main( CancellationTokenSource source = tasks = Task[ ( i = ; i < ; i++ tasks[i] = Task.Factory.StartNew((m) => Task.WhenAll(tasks).ContinueWith((t) => Console.Writeline( Environment.Exit( input = ( ( (token.IsCancellationRequested) Thread.Sleep( Console.Writeline( }

       单从代码量上面看就缩减了17行代码,而且业务逻辑也非常的简单明了,然后再看业务逻辑方法Run,其实你根本就不需要所谓的workThreadNums++,--

的 *** 作,而且多线程下不用锁的话,还容易出现竞态的问题,解决方案就是使用WhenAll等待一组Tasks完成任务,之后再串行要退出的Task任务,是不是很完美,

而协作取消的话,只需将取消的token传递给业务逻辑方法,当主线程执行source.Cancel()方法取消的时候,子线程就会通过IsCancellationRequested感知到主

线程做了取消 *** 作。

好了,就说这么多吧,还是那句话,”“,所以古话说得好,磨刀不误砍柴工。。。

总结

以上是内存溢出为你收集整理的使用Task的一些知识优化了一下同事的多线程协作取消的一串代码全部内容,希望文章能够帮你解决使用Task的一些知识优化了一下同事的多线程协作取消的一串代码所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1264097.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-08
下一篇 2022-06-08

发表评论

登录后才能评论

评论列表(0条)

保存