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

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

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

一:现实场景

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

 class Program
{
private static int workThreadNums = ; private static bool isStop = false; static void Main(string[] args)
{
var tasks = new Task[]; for (int i = ; i < ; i++)
{
tasks[i] = Task.Factory.StartNew((obj) =>
{
Run();
}, i);
} //是否退出
string input = Console.ReadLine(); while ("Y".Equals(input, StringComparison.OrdinalIgnoreCase))
{
break;
} isStop = true; while (workThreadNums != )
{
Console.WriteLine("正在等待线程结束,当前还在运行线程有:{0}", workThreadNums); Thread.Sleep();
}
Console.WriteLine("准备退出了。。。");
Console.Read();
Environment.Exit();
} static void Run()
{
try
{
workThreadNums++; while (true)
{
if (isStop) break; Thread.Sleep(); //执行业务逻辑
Console.WriteLine("我是线程:{0},正在执行业务逻辑", Thread.CurrentThread.ManagedThreadId);
}
}
finally
{
workThreadNums--;
}
}
}

其实扫一下上面的代码应该就知道是用来干嘛的,业务逻辑没毛病,基本可以实现刚才的业务场景,在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之后就可以了。好了,上

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

  class Program
{
static void Main(string[] args)
{
CancellationTokenSource source = new CancellationTokenSource(); var tasks = new Task[]; for (int i = ; i < ; i++)
{
tasks[i] = Task.Factory.StartNew((m) =>
{
Run(source.Token);
}, i);
} Task.WhenAll(tasks).ContinueWith((t) =>
{
Console.WriteLine("准备退出了。。。");
Console.Read();
Environment.Exit();
}); string input = Console.ReadLine();
while ("Y".Equals(input, StringComparison.OrdinalIgnoreCase))
{
source.Cancel();
} Console.Read();
} static void Run(CancellationToken token)
{
while (true)
{
if (token.IsCancellationRequested) break; Thread.Sleep(); //执行业务逻辑
Console.WriteLine("我是线程:{0},正在执行业务逻辑", Thread.CurrentThread.ManagedThreadId);
}
}
}

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

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

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

线程做了取消操作。

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

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

  1. java 性能优化:35 个小细节,让你提升 java 代码的运行效率

    前言 代码 优化 ,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没 ...

  2. (转).NET 4.5中使用Task.Run和Parallel.For()实现的C# Winform多线程任务及跨线程更新UI控件综合实例

    http://2sharings.com/2014/net-4-5-task-run-parallel-for-winform-cross-multiple-threads-update-ui-dem ...

  3. SSE图像算法优化系列六:OpenCv关于灰度积分图的SSE代码学习和改进。

    最近一直沉迷于SSE方面的优化,实在找不到想学习的参考资料了,就拿个笔记本放在腿上翻翻OpenCv的源代码,无意中看到了OpenCv中关于积分图的代码,仔细研习了一番,觉得OpenCv对SSE的灵活运 ...

  4. Java 知识笔记 - 类、集合、多线程、IO、JVM(最后一次更新,2019年02月17日)

    目录 Class 内部类.静态内部类.匿名内部类.局部内部类 Collection Java Collection Set Queue Map Collections Arrays System Co ...

  5. 架构相关:组件化/模块化/工程化/性能优化/开发规范与团队协作/组件间调用与通信(flex/redux)/调试与测试

    https://github.com/fouber/blog http://teropa.info/blog/2015/09/10/full-stack-redux-tutorial.html htt ...

  6. ajax——三级联动下拉列表框的优化(简化页面,用jquery插件代替原来页面代码,返回处理数据类型为"TEXT")

    数据库: 主页面 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://ww ...

  7. 《Android应用性能优化》 第5章 多线程和同步

    1.DDMS中可以看见的系统线程(Andorid3.1的Galaxy Tab 10.1为例): main HeapWorker 执行finalize函数和引用对象清理 GC Garbage Colle ...

  8. 【优化算法】变邻域搜索算法解决0-1背包问题(Knapsack Problem)代码实例 已

    01 前言 经过小编这几天冒着挂科的风险,日日修炼,终于赶在考试周中又给大家更新了一篇干货文章.关于用变邻域搜索解决0-1背包问题的代码.怎样,大家有没有很感动? 02 什么是0-1背包问题? 0-1 ...

  9. 【知识】location.search获取中文时候会被编码成一串字符

    [转码] 例如:case.html?id='这个是页面的标题' 当想要使用location.search获取?id='这个是页面的标题'的时候,包含的中文会被编码成一串字符串. 所以我们需要进行解码, ...

随机推荐

  1. Java 验证用户名、密码

    1. 数据库操作 2.验证用户 2.1. 查询 String sql = String.format("select count(*) from user where name='%s' a ...

  2. Cracking the code interview

    推荐一本书<Cracking the code interview> Now in the 5th edition, Cracking the Coding Interview gives ...

  3. C#综合揭秘——细说进程、应用程序域与上下文之间的关系

    引言 本文主要是介绍进程(Process).应用程序域(AppDomain)..NET上下文(Context)的概念与操作.虽然在一般的开发当中这三者并不常用,但熟悉三者的关系,深入了解其作用,对提高 ...

  4. js实现数组去重并且显示重复的元素和索引值

    var arr=["a","b","c","d","c","b","d ...

  5. Word常用实用知识2

      纯手打,可能有错别字,使用的版本是office Word 2013  转载请注明出处 http://www.cnblogs.com/hnnydxgjj/p/6296863.html,谢谢. 批注和 ...

  6. [源代码] SailingEase .NET Resources Tool (.NET 多语言资源编辑器)

    我在2016年10月发过一篇博客,介绍了我写过的一个多语言资源文件编辑器,并且做为免费软件发布给了出来. 陆续收到了一些朋友的反馈,有朋友很热心提了很多建议和关心的话,还有朋友发红包过来,让我很感动. ...

  7. 基于服务的SOA架构_后续篇

    今天是元宵节,首先祝各位广大博友在接下来的光阴中技术更上一层,事事如意! 昨天简单介绍了一下本人在近期开发过的一个电商购物平台的架构流程和一些技术说明:今天将详细总结一下在项目中用到的各个架构技术的环 ...

  8. PHP面向对象——GD库实现图片水印和缩略图

    今天的实现目标就是使用GD库完成对图片加水印和图 片缩略图两个功能 动身前逻辑准备 属性: 路径 功能: 构造方法 生成水印的方法 获取 图片信息 获取位置信息(123 456 789) 创建图片资源 ...

  9. (3)activiti流程的挂起和激活

    有时候,我们需要对一个已经执行的流程进行暂停,而不是删除它,这个时候就需要我们调用activiti暂停和激活的api来操作他们 每启动一个流程实例,都会在该流程实例下产生相应的流程任务,处于1*多的关 ...

  10. Windows环境下安装配置Teamcity配合git自动发布mvc,webapi站点

    以下是本人配置Teamcity具体环境和步骤,只实现了项目发布,打包.Nodejs npm gulp没有配置成功,后期补上. 1 环境安装 本人使用的是windows7 sp1 64位系统,(.net ...