前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >并发编程 ---为何要线程池化

并发编程 ---为何要线程池化

作者头像
Niuery Diary
发布于 2023-10-22 08:47:31
发布于 2023-10-22 08:47:31
20500
代码可运行
举报
运行总次数:0
代码可运行

引言

众所周知,使用线程可以极大的提高应用程序的效率和响应性,提高用户体验,但是不可以无节制的使用线程,为什么呢?

线程的开销

线程的开销实际上是非常大的,我们从空间开销和时间开销上分别讨论。

线程的空间开销

线程的空间开销来自这四个部分:

  1. 线程内核对象(Thread Kernel Object)。每个线程都会创建一个这样的对象,它主要包含线程上下文信息,在32位系统中,它所占用的内存在700字节左右。
  2. 线程环境块(Thread Environment Block)。TEB包括线程的异常处理链,32位系统中占用4KB内存。
  3. 用户模式栈(User Mode Stack),即线程栈。线程栈用于保存方法的参数、局部变量和返回值。每个线程栈占用1024KB的内存。要用完这些内存很简单,写一个不能结束的递归方法,让方法参数和返回值不停地消耗内存,很快就会发生 OutOfMemoryException
  4. 内核模式栈(Kernel Mode Stack)。当调用操作系统的内核模式函数时,系统会将函数参数从用户模式栈复制到内核模式栈。在32位系统中,内核模式栈会占用12KB内存。

线程的时间开销

线程的时间开销来自这三个过程:

  1. 线程创建的时候,系统相继初始化以上这些内存空间。
  2. 接着CLR会调用所有加载DLL的DLLMain方法,并传递连接标志(线程终止的时候,也会调用DLL的DLLMain方法,并传递分离标志)。
  3. 线程上下文切换。一个系统中会加载很多的进程,而一个进程又包含若干个线程。但是一个CPU内核在任何时候都只能有一个线程在执行。为了让每个线程看上去都在运行,系统会不断地切换“线程上下文”:每个线程及其短暂的执行时间片,然后就会切换到下一个线程了。 这个线程上下文切换过程大概又分为以下5个步骤:
    • 步骤1进入内核模式。
    • 步骤2将上下文信息(主要是一些CPU寄存器信息)保存到正在执行的线程内核对象上。
    • 步骤3系统获取一个 Spinlock ,并确定下一个要执行的线程,然后释放 Spinlock 。如果下一个线程不在同一个进程内,则需要进行虚拟地址交换。
    • 步骤4从将被执行的线程内核对象上载入上下文信息。
    • 步骤5离开内核模式。

所以,由于要进行如此多的工作,所以创建和销毁一个线程就意味着代价“昂贵”,即使现在的CPU多核多线程,如无节制的使用线程,依旧会严重影响性能。

引入线程池

为了避免程序员无节制地使用线程,微软开发了“线程池”技术。简单来说,线程池就是替开发人员管理工作线程。当一项工作完毕时,CLR不会销毁这个线程,而是会保留这个线程一段时间,看是否有别的工作需要这个线程。至于何时销毁或新起线程,由CLR根据自身的算法来做这个决定。

线程池技术能让我们重点关注业务的实现,而不是线程的性能测试

微软除实现了线程池外,还需要关注一个类型:BackgroundWorkerBackgroundWorker 是在内部使用了线程池的技术:同时,在WinForm或WPF编码中,它还给工作线程和UI线程提供了交互的能力。

实际上, ThreadThreadPool 默认都没有提供这种交互能力,而 BackgroundWorker 却通过事件提供了这种能力。这种能力包括:报告进度、支持完成回调、取消任务、暂停任务等。

BackgroundWorker 的简单示例如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private BackgroundWorker backgroundWorker = new BackgroundWorker();

private void AsyncButton_Click(object sender, RoutedEventArgs e)
{
    //注册要执行的任务
    backgroundWorker.DoWork += BackgroundWorker_DoWork;
    //注册报告进度
    backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged;
    //注册完成时的回调
    backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
    //设置允许任务取消
    backgroundWorker.WorkerSupportsCancellation = true;
    //设置允许报告进度
    backgroundWorker.WorkerReportsProgress = true;
    backgroundWorker.RunWorkerAsync();
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
    //取消任务
    if (backgroundWorker.IsBusy)
        backgroundWorker.CancelAsync();
}
private void BackgroundWorker_RunWorkerCompleted(object? sender, RunWorkerCompletedEventArgs e)
{
    //完成时回调
    MessageBox.Show("BackgroundWorker RunWorkerCompleted");
}

private void BackgroundWorker_ProgressChanged(object? sender, ProgressChangedEventArgs e)
{   
    //报告进度
    this.textbox.Text = e.ProgressPercentage.ToString();
}

private void BackgroundWorker_DoWork(object? sender, DoWorkEventArgs e)
{
    BackgroundWorker? worker = sender as BackgroundWorker;

    if (worker != null)
    {
        for (int i = 0; i < 20; i++)
        {
            if (worker.CancellationPending)
            {
                e.Cancel = true;
                break;
            }
            worker.ReportProgress(i);

            Thread.Sleep(100);
        }
    }
}

建议使用WinForm和WPF的开发人员使用 BackgroundWorker

Task替代ThreadPool

ThreadPool 相对于 Thread 来说具有很多优势,但是 ThreadPool 在使用上却存在一定的不方便。比如:

  • ThreadPool 不支持线程的取消、完成、失败通知等交互性操作。
  • ThreadPool 不支持线程执行的先后次序。

所以随着 Task 类及其所提供的异步编程模型的引入,Task相较ThreadPool具有更多的优势。大概有一下几点:

  1. Task是.NET Framework的一部分,它提供了更高级别的抽象来表示异步操作或并发任务。相比之下,ThreadPool较为底层,需要手动管理线程池和任务队列。通过使用Task,我们可以以更简洁、更可读的方式表达并发逻辑,而无需关注底层线程管理的细节。
  2. Task是基于Task Parallel Library(TPL)构建的核心组件,它提供了强大的异步编程支持。利用Task,我们能够轻松定义异步方法、等待异步操作完成以及处理任务结果。与此相反,ThreadPool主要用于执行委托或操作,缺乏直接的异步编程功能。
  3. Task在底层使用ThreadPool来执行任务,但它提供了更优秀的性能和资源管理机制。通过使用Task,我们可以利用TPL提供的任务调度器,智能化地管理线程池的大小、工作窃取算法和任务优先级。这样一来,我们能够更有效地利用系统资源,并获得更好的性能表现。
  4. Task拥有强大的任务关联和组合功能。我们可以使用Task的 ContinueWith()When()WhenAll()Wait()等方法定义任务之间的依赖关系,以及在不同任务完成后执行的操作。这种任务组合方式使并发编程更加灵活且易于管理。
  5. Task提供了更好的异常处理和取消支持机制。我们可以利用Task的异常处理机制捕获和处理任务中的异常,而不会导致整个应用程序崩溃。此外,Task还引入 CancellationToken 的概念,可用于取消任务的执行,从而更好地控制并发操作。

所以,尽管ThreadPool在某些情况下仍然有其用途,但在C#编程中,使用Task替代ThreadPool已变为通用实践,推荐优先考虑使用Task来处理并发任务。

❝以上部分内容引用自 《编写高质量代码:改善C#程序的157个建议》 / 陆敏技著.一北京:机械工业出版社,2011.9

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-06-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Niuery Diary 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
基于龙芯的Linux源码学习 | 入门篇
最近在中标麒麟实习,自己对 Linux 本身也有一定爱好,使用过不少的 GNU/Linux 发行版,正好看到陈华才老师新出版了《基于龙芯的 Linux 内核探索解析》一书,于是也下载源码跟着老师一同学习,博客开这个大坑也是准备记录自己的学习历程。
uniartisan
2022/03/03
7190
让Typecho支持Emoji
本文作者:博主: gyrojeff    文章标题:让Typecho支持Emoji 本文地址:https://gyrojeff.top/index.php/archives/17/ 版权说明:若无注明,本文皆为“gyro永不抽风!”原创,转载请保留文章出处。 许可协议:署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 转载请保留原文链接及作者! 我的博客即将同步至腾讯云+社区,邀请大家一同入驻
gyro永不抽风
2021/05/21
6300
使Typecho支持最流行的Emoji表情
Typecho默认不支持emoji表情,其实不是程序的锅,而是由于编码的问题,只需要将默认的数据库编码utf8修改为utf8mb4即可,当然别忘了,utf8mb4编码只有在PHP5.5以后才支持。 简单两步即可让typecho支持emoji
逍遥子大表哥
2021/12/17
6520
使Typecho支持最流行的Emoji表情
typecho支持emoji
今天也是突发奇想的购买更换了一个主题,虽然破费了我兜里仅有的58r。但有一说一的是这主题确实不错,唯一不足之处就是没能让作者把他的审美传授给我
是小北a
2022/03/28
6400
typecho支持emoji
Typecho发布文章Database Query Error-星泽V社
刚才发布文章出现 Database Query Error,百度了问题,原因是 Typecho 在初始化数据库的时候,选用了 UTF-8 的编码,而在 MySQL 中,UTF-8 只支持最多 3 个字节,而 emoji 是 4 个字节,所以出现该问题。如果嫌麻烦可以直接删掉 emoji 就可以成功发表了。
星泽V社
2022/04/14
1.6K0
Typecho发布文章Database Query Error-星泽V社
解决Typecho带有Emoji表情Database Query Error报错的处理方法!-星泽V社
今天写文章的时候插入emoji表情竟然「Database Query Error」报错,Typecho 默认不支持 Emoji 表情,这不是程序的锅,而是由于编码的问题造成。 Emoji 是一种在 Unicode 位于u1F601-u1F64F区段的字符。这显然超出了目前常用的 UTF-8 字符集的编码范围u0000-uFFFF。 在 MySQL 中,UTF-8 只支持最多 3 个字节,而 Emoji 是 4 个字节。所以评论中带有 Emoji 表情才会报错。
星泽V社
2022/03/02
1K0
解决Typecho带有Emoji表情Database Query Error报错的处理方法!-星泽V社
Typecho添加emoji表情
1、修改数据库编码 在PhpMyadmin中选择typecho数据库,操作-->排序规则-->选择utf8mb4_unicode_ci然后执行。 或者在服务器中执行(通用版 ) 首先在服务器中执行
云计算小黑
2022/12/28
4650
Typecho中emoji表情不显示
Typecho 默认不支持 Emoji 表情,由于编码的问题而造成,我们只需简单两步即可让 typecho 支持 emoji 表情评论,只要将默认的数据库编码 utf8 修改为 utf8mb4 即可。
qiangzai
2021/12/21
5690
Typecho中emoji表情不显示
让Typecho支持 emoji 的显示
今天有用户反应文章发出后显示不全,我第一时间就想到是自己模板外链转内链功能的bug,因为之前有人反馈过,后来我修复了,我以为还是它有问题,排查了下后发现并不是,然后后台一看文章内容,发现基本全消失了,这就让我想起来hran大佬2015年发过的文章,于是乎转载之
泽泽社长
2023/04/17
2550
让typecho支持emoji
这年头文章里加个emoji很正常吧,但是typecho貌似之前没有考虑到,没有原生支持emoji,但是这怎么能难倒我们勤劳的码农呢!
老高的技术博客
2022/12/27
3840
如何在Typecho显示Emoji表情
解决方法 1.先进入数据库面板,点击SQL运行下列语句(粘贴SQL语句执行即可): SQL语句 alter table typecho_comments convert to character
RJ1027
2021/08/09
1.1K0
如何在Typecho显示Emoji表情
建情人节表白网站(超详细过程,包教包会)
声明:此文章以经过原作者允许进行的二次创作,原作者的博文如下,感谢作者Veen Zhao,带来漂亮的主题
wresource
2022/07/31
1.1K0
建情人节表白网站(超详细过程,包教包会)
两行代码使typecho支持Emoji表情
说明最近实在不知道该写些啥了,由于主题的自带表情还是较为缺少,于是我自己添加了一些表情,所以在此分享一下表情添加方...
Xcnte
2021/12/14
6580
存储emoji表情或特殊字符报错(Incorrect string value: '\xF0\x9F\x98\x82\xF0\x9F...')
我在一篇文章中使用了emoji表情,前面很顺利,不管是WordPress还是其他博客园啥的,都是正常发送。
JanYork_简昀
2022/05/18
6300
存储emoji表情或特殊字符报错(Incorrect string value: '\xF0\x9F\x98\x82\xF0\x9F...')
搭建Typecho博客
https://qqdie.com/plugins/ https://plugins.typecho.me/
MiChong
2020/09/24
1.4K0
搭建Typecho博客
typecho使用emoji表情
1.什么是emoji表情 2.登录phpmyadmin 3.修改服务器连接排序规则 4.选择博客的数据表进行更改 5.总结
堡主
2023/03/04
6740
typecho使用emoji表情
Mysql存储微信Emoji表情问题
上面错误意思是 mysql 数据库中 name 字段插入不正确的字符串值。name 字段是记录微信呢称,设计之出没有考虑到微信呢称中使用 Emoji 表情,导致写入数据失败。
YP小站
2020/06/04
2K0
在Django中使MySQL支持存储Emoji表情????
当我们在Django应用程序中使用MySQL数据库时,可能会遇到无法存储包含Emoji表情的数据的问题。这是因为MySQL默认字符集和校对规则只支持存储基本的Unicode字符集,无法支持Emoji表情字符。在这篇博客中,我将为您介绍如何在Django中使MySQL支持存储Emoji表情。
Hopetree
2023/05/03
9960
MySQL字符集utf8mb4
Unicode是编码字符集,而UTF-8就是字符编码,即Unicode规则字库的一种实现形式。随着互联网的发展,对同一字库集的要求越来越迫切,Unicode标准也就自然而然的出现。它几乎涵盖了各个国家语言可能出现的符号和文字,并将为他们编号。
HLee
2021/07/23
1.6K0
MySQL字符集utf8mb4
MySQL utf8mb4 编码 : 支持emoji表情 (字段或表)
一般我们数据编码格式默认设置为utf-8, 但是utf-8并不支持表情,而表情需要utf8mb4字段(向下兼容utf-8)
一个会写诗的程序员
2020/04/16
4.1K0
相关推荐
基于龙芯的Linux源码学习 | 入门篇
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档