买椟还珠

上一篇《C#中多线程的那点事儿-Thread入门》,我们掰扯了一下Thread的最基本用法。

我们说到,多线程,可以利用CPU的多个核心,并行执行,从而提升程序的效率。

有个聪明的同学,小明,向我问到:那是不是线程开得越多,程序运行就越快呢?

为了搞明白这个问题,外老师专门写了个测试程序,用来掰扯掰扯多线程的代价那点事。

实例演练

先来一个Data类,模拟某种数据:

class Data{    public Data(int id)    {        ID = id;    }    public int ID { get; set; }    public void DoSomeThing()    {        ID += 1;    }}

Data.DoSomeThing模拟某种操作,这里简单在做一个加法。

然后我们生成一批测试数据:

static IList MakeData(){    var dataNum = 100;    var datas = new List(dataNum);    for (int i = 0; i < dataNum; i++)    {        datas.Add(new Data(i));    }    return datas;}

下面用单线程调用全部数据的DoSomeThing方法,来模拟批量数据处理:

static void SingleThread(IList datas){    foreach (var item in datas)    {        item.DoSomeThing();    }}

下面是一个不正确的多线程模拟批量数据处理的函数:

static void MultiThread(IList datas){    foreach (var item in datas)    {        var thd = new System.Threading.Thread(            () => item.DoSomeThing());        thd.Start();        // 这里先不管 thd.Join();        // 也不要求 DoSomeThing 始终执行    }}

然后编写测试程序,对以上两个函数进行执行速度比较:

static void Main(string[] args){    Console.WriteLine("Hello Thread World!");    var dts = MakeData();    var sw = new Stopwatch();    sw.Start();    SingleThread(dts);    sw.Stop();    Console.WriteLine($"Single Thread Time Costs: {sw.ElapsedTicks}");    sw.Restart();    MultiThread(dts);    sw.Stop();    Console.WriteLine($"Multi Thread Time Costs: {sw.ElapsedTicks}");    Console.ReadKey();}

由于我们的数据量不大,我们使用CPU的ticks数量,来计时(要不然看不出来区别),相信有经验的同学,已经知晓运行结果了:

多线程反而更慢

可以看到,多线程运行消耗的时间,是单线程的200倍!而且我们还没有等待所有线程执行完毕。是不是让人大跌眼镜!

小明看到这里,心里已经有了初步的答案了。但是外老师还是从他满脸的问号当中,看出了他心中的疑惑:这是什么呢?

线程的代价

上面是一个极端的反面教材,我的目的,就是要让大家意识到,多线程不是万能的。有的时候,开启10个线程,远远达不到10倍的执行速度提升的效果。

这背后的原因,是因为创建线程是有代价的!而且这个代价非常的昂贵。那不是一般的昂贵!

  • 线程需要的内存

我们再来写一个测试程序,在程序中开启一定数量的空线程(尽量保证线程执行的代码不再占用额外的内存),然后调整线程的数量,来观察线程的内存占用情况:

static void Main(){    Console.WriteLine("Hello Thread World!");    var thds = new List();    var thdnum = 100;  // 线程数量    for (int i = 0; i < thdnum; i++)    {        var t = new System.Threading.Thread(TestThread);        t.Start();        thds.Add(t);    }    Console.WriteLine("Thread Finished!");    Console.ReadKey();}static void TestThread(){    Console.ReadKey();}

上面是100个空线程,编译程序,并在Release模式下运行,查看内存的占用情况如下:

100个线程的内存占用

可以看到,占用的内存并不多,只有14.0MB。然后我们将线程数量调整为1万个试试:

1万个线程的内存占用

我们看到内存从14.0MB上升到了306.1MB,看起来感觉还不错,内存占用不是特别离谱。但是我的电脑,已经有明显的卡顿了!

我掏出计算器算了算,一个线程占用的内存约为:0.03MB,也就是30KB的样子:

单个线程平均内存占用

可能有同学要说了,感觉还行,可以接受!但是当我尝试开启10万个线程的时候,卡了很久,也没有成功!而且电脑变得几乎不可操作了,基本卡住不动了!无奈我只好强制结束了测试程序的进程。

MMP

也就是说,用C#来处理类似游戏这类长连接的场景的话,其同时连接的用户数,很难突破10万。当然,真正的游戏后台,一般不会选择C#来做。而是选择更高效的C++或者其他语言。

聪明小明,又问道:这30KB的内存,线程拿去做什么了呢?

其实就是用来保存线程的上下文的。比如堆栈信息,局部变量等等。这此信息,CPU在进行不同线程的切换执行的时候,需要用来恢复线程的状态。

  • 线程需要的CPU

现在我们再回到最开始的示例,示例中,多线程花费的时间,比单线程要多得多得多,多了好几个数量级。这在程序界,是非常大的性能差异。

其实线程除了需要内存还支撑之外,还需要占用大量的CPU资源。主要是操作系统调度这些线程,执行线程的上下文切换等工作,也需要占用大量CPU资源。

还有很重要的一点,在销毁线程的时候,同样会耗费大量CPU资源。

所以如果我们的程序中的任务,是非常简单的任务,千万不能一个任务开启一个线程。因为线程本身的开销,要远大于我们的任务本身的开销。不能为了多线程这个漂亮盒子,而去做买椟还珠的愚蠢买卖。

总结

好了,说了这么多,只是向大家说明,线程的内存和CPU的占用情况。内存和CPU是我们电脑中非常稀缺的宝贵资源,一定要将好钢用到刀刃上。而不是将其浪费在毫无意义的多线程上下文切换上面。

希望大家可以意识到这个昂贵的代价,不要随意滥用线程。

小明默默的点头

这时我看到小明,在那默默的望着我点头,怀里还抱着个大红包,这是要送给外老师吗?

踩坑记录

实在是太基础了,没有踩到坑!

下期预告

下面是给同学们准备的干货,陆续发货中哦:

ThreadPool

多个线程之间的资源共享问题

多线程死锁问题

Task

Parallel

await/async

Linq与PLinq (ParallelEnumerable)

。。。 。。。

多线程处理同一批数据_C#中多线程的那点事-多线程的代价相关推荐

  1. 多线程处理同一批数据_多进程和多线程的优缺点

    来源:http://www.cnblogs.com/Yogurshine/p/3640206.html 在Linux下编程多用多进程编程少用多线程编程. IBM有个家伙做了个测试,发现切换线程cont ...

  2. c 语言bool 类型数据_C ++中的bool数据类型

    c 语言bool 类型数据 In C++ programming language, to deal with the Boolean values – C++ added the feature o ...

  3. vue获取接口数据_c#中HttpWebRequest调用接口获取数据

    c#中HttpWebRequest调用接口获取数据PART  01-封装接口 将post请求进行封装,以便调用使用: /// /// 请求后台地址/// 请求参数/// public string P ...

  4. 多线程分批量处理list数据_使用多线程处理输入的数据

    TensorFlow 的 Session 对象是支持多线程的,因此多个线程可以很方便地在同一个会话下对同一个队列并行地执行操作.Python 本身也提供了创建线程的threading.py,这个文件提 ...

  5. 【多线程编程学习】java多线程基于数据分割的大文件下载器

    文章目录 代码:基于数据分割的大文件下载器 作为包装的存储对象类: 主文件下载类: 子任务下载类: 处理缓存: 启动类: 数据分割思想产生的问题 代码来自书籍<java多线程编程实战指南> ...

  6. Redis基本使用及百亿数据量中的使用技巧分享

    作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9941208.html 作者:大石头 时间:2018-11-10 晚上20:00 地点:钉钉群(组织代码B ...

  7. auto.js停止所有线程_使用多线程处理输入的数据

    TensorFlow 的 Session 对象是支持多线程的,因此多个线程可以很方便地在同一个会话下对同一个队列并行地执行操作.Python 本身也提供了创建线程的threading.py,这个文件提 ...

  8. 过滤一批数据_手把手教你学numpy,从此数据处理不再慌【三】

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是numpy专题的第三篇,我们来聊聊numpy当中的索引. 上篇的末尾其实我们简单地提到了索引,但是没有过多深入.没有过多深入的原因也很 ...

  9. 独家 | 手把手教你从有限的数据样本中发掘价值(附代码)

    作者:Bety Rodriguez-Milla 翻译:和中华 校对:吴金笛 本文约2800字,建议阅读8分钟. 本文展示了当数据稀缺时,如何一步步进行分析从而得到一些见解. [ 导读 ]本文是系列文章 ...

最新文章

  1. Google Chrome(谷歌浏览器) 发布下载
  2. linux下echo指令
  3. Linux Socket学习(十三)
  4. 数据仓库dw层_数据仓库分层之辩
  5. TCP之超时重传机制
  6. c mysql 时间段查询_mySql 时间段查询
  7. WordPress 主题教程 #4b:Header 模板 2
  8. vector容器动态申请内存的过程_记录一次自定义Allocator profile的过程
  9. 170819-关于JSTL的知识点
  10. 2017年最具价值的十大开源项目:tensorflow 第一
  11. 【社招】量化研究员(机器学习)-Akuna Capital -上海
  12. 看Vue文档总结之路(四)
  13. Python比较文本相似度的7种方法(详细)
  14. vue + cesium加载krigingjs插件库生成等值线图
  15. 我的世界java皮肤展开图,我的世界情侣皮肤,我的世界皮肤展开图做图片
  16. Parameter 参数与 Argument 参数
  17. 巴什博弈--Nim游戏
  18. AppID、AppKey、AppSecret
  19. php判断关联数组为空,php 关联数组判断是否为空
  20. Firefox配置阿里云DNS方法

热门文章

  1. UI设计素材|底部导航设计的黄金法则
  2. C4d模型--电商广告海报三维模型
  3. PSD分层电商促销模板|换季大促销,不怕老板催你做海报了
  4. 进程(Process)和线程(Thread)的区别
  5. Android的HashMap方法,Android中实现HashMap排序的方法
  6. matplotlib之legend图例和标注(笔记三)
  7. Java代理模式学习
  8. linux sed后 保存文本,实例详解linux文本三剑客--sed
  9. mysql数据库主从不同步_mysql数据库主从不同步的解决方法
  10. python3装饰器例子_Python装饰器几个有用又好玩的例子