首页 > 其他分享 >使用Task的一些知识优化了一下同事的多线程协作取消的一串代码

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

时间:2023-04-18 12:38:53浏览次数:36  
标签:10 逻辑 Task Console 代码 业务 协作 线程 多线程

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

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

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

 

一:现实场景

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

 1 class Program
 2     {
 3         private static int workThreadNums = 0;
 4 
 5         private static bool isStop = false;
 6 
 7         static void Main(string[] args)
 8         {
 9             var tasks = new Task[10];
10 
11 
12             for (int i = 0; i < 10; i++)
13             {
14                 tasks[i] = Task.Factory.StartNew((obj) =>
15                 {
16                     Run();
17                 }, i);
18             }
19 
20             //是否退出
21             string input = Console.ReadLine();
22 
23             while ("Y".Equals(input, StringComparison.OrdinalIgnoreCase))
24             {
25                 break;
26             }
27 
28             isStop = true;
29 
30             while (workThreadNums != 0)
31             {
32                 Console.WriteLine("正在等待线程结束,当前还在运行线程有:{0}", workThreadNums);
33 
34                 Thread.Sleep(10);
35             }
36             Console.WriteLine("准备退出了。。。");
37             Console.Read();
38             Environment.Exit(0);
39         }
40 
41         static void Run()
42         {
43             try
44             {
45                 workThreadNums++;
46 
47                 while (true)
48                 {
49                     if (isStop) break;
50 
51                     Thread.Sleep(1000);
52 
53                     //执行业务逻辑
54                     Console.WriteLine("我是线程:{0},正在执行业务逻辑", Thread.CurrentThread.ManagedThreadId);
55                 }
56             }
57             finally
58             {
59                 workThreadNums--;
60             }
61         }
62     }

 

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

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

 

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

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

 

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

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

 

二:代码优化

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

 

1. 如何实现多线程中的协作取消?

2. 如何实现多线程整体执行完毕通知主线程?

 

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

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

 

1. 协作取消:

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

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

 

2. 多线程整体执行完毕通知主线程

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

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

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

 1  class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             CancellationTokenSource source = new CancellationTokenSource();
 6 
 7             var tasks = new Task[10];
 8 
 9             for (int i = 0; i < 10; i++)
10             {
11                 tasks[i] = Task.Factory.StartNew((m) =>
12                 {
13                     Run(source.Token);
14                 }, i);
15             }
16 
17             Task.WhenAll(tasks).ContinueWith((t) =>
18             {
19                 Console.WriteLine("准备退出了。。。");
20                 Console.Read();
21                 Environment.Exit(0);
22             });
23 
24             string input = Console.ReadLine();
25             while ("Y".Equals(input, StringComparison.OrdinalIgnoreCase))
26             {
27                 source.Cancel();
28             }
29 
30             Console.Read();
31         }
32 
33         static void Run(CancellationToken token)
34         {
35             while (true)
36             {
37                 if (token.IsCancellationRequested) break;
38 
39                 Thread.Sleep(1000);
40 
41                 //执行业务逻辑
42                 Console.WriteLine("我是线程:{0},正在执行业务逻辑", Thread.CurrentThread.ManagedThreadId);
43             }
44         }
45     }

 

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

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

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

线程做了取消操作。

 

好了,就说这么多吧,还是那句话,”因为我们视野的不开阔,导致缺乏解决问题的手段“,所以古话说得好,磨刀不误砍柴工。。。

标签:10,逻辑,Task,Console,代码,业务,协作,线程,多线程
From: https://blog.51cto.com/u_15353947/6202806

相关文章

  • 同步异步多线程这三者关系,你能给面试官一个满意的回答吗?
    前几天一位朋友去面试,面试官问了他同步,异步,多线程之间是什么关系,异步比同步高效在哪?多线程比单线程高效在哪?由于回答的不好,让我帮他捋一下,其实回答这个问题不难,难就难在只对别人说理论,而没有现杀的例子。一:异步1.到底解放了谁?<1>从基础的同步说起要说解放了谁,一定得有几......
  • 多线程
    本文按照Unix环境高级编程总结而成:线程概念典型的进程可以看成只有一个控制线程:一个进程在某一时刻只能做一件事情。有了多个线程以后,就可以把进程设计成在某一时刻能够做多件事情,每个线程各自处理独立的任务。这种设计的好处有:通过为每种事件类型分配单独的处理线程,可以......
  • 如何玩转国产神器:接口一体化协作平台Apifox!
    前言:Apifox是什么?简介:简单来说,Apifox=swagger+mock+postman+Jmeter,是API文档、API调试、APIMock、API自动化测试一体化协作平台。可以把接口开发过程中各角色的工作,例api设计者、后端开发、前端开发、测试人员协同到一个Apifox平台完成。工具使用界面的各个功能都直......
  • 这才是最好用的Office软件?OnlyOffice协作办公软件使用体验
    这才是最好用的Office软件?OnlyOffice协作办公软件使用体验(baidu.com)说起Office办公软件,我想大家最常用的应该就是微软的MicrosoftOffice以及国产的WPSOffice,这两款办公软件优点明显,不过我相信大家和我一样也发现了其存在的明显不足,前者是付费软件,而后者有广告弹窗,总结......
  • 「ONLYOFFICE」一个全能免费神仙级的开源协作办公套件
    「ONLYOFFICE」一个全能免费神仙级的开源协作办公套件(baidu.com) 【ONLYOFFICE】一个全能免费神仙级的开源协作办公套件!附NAS安装部署教程  说到Office办公套件,我想目前绝大多数人的第一印象想到的应该就是MicrosoftOffice和WPS。MicrosoftOffice作为微软自带的Off......
  • C++实现多线程
    #include<iostream>#include<chrono>#include<thread>voidprintNumbers1(){for(inti=1;i<=10000;i++){std::cout<<"Thread1:"<<i<<std::endl;}}voidprintNumbers2(){for......
  • 第5章 高效的多线程日志
    日志库介绍:一个日志库大体可分为前端(frontend)和后端(backend)两部分。前端是供应用程序使用的接口(API),并生成日志消息(logmessage);后端则负责把日志消息写到目的地(destination)。在多线程程序中,前端和后端都与单线程程序无甚区别,无非是每个线程有自己的前端,整个程序共用一个后端。但难点......
  • ThreadPoolTaskExecutor和ThreadPoolExecutor区别
    ThreadPoolExecutor是Java原生的线程池类,而ThreadPoolTaskExecutor是Spring推出的线程池工具  一、从核心参数看两者关系 ThreadPoolExecutor(java.util.concurrent) publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,......
  • 频繁设置CGroup触发linux内核bug导致CGroup running task不调度
    1.说明1>本篇是实际工作中linux上碰到的一个问题,一个使用了CGroup的进程处于R状态但不执行,也不退出,还不能kill,经过深入挖掘才发现是Cgroup的内核bug2>发现该bug后,去年给RedHat提交过漏洞,但可惜并未通过,不知道为什么,这里就发我博客公开了3>前面的2个帖子《极简cfs公平调度算......
  • 多线程
    一.多线程1.什么是线程要了解线程,首先需要知道进程。一个进程指的是一个正在执行的应用程序。线程对应的英文名称为“thread”,它的功能是执行应用程序中的某个具体任务,比如一段程序、一个函数等。线程和进程之间的关系,类似于工厂和工人之间的关系,进程好比是工厂,线程就如同工厂......