C++多线程多少个线程算多?

程序一启动就创建了60个线程,太多了,应该控制一下。也有同学提出不同意见,说线程多不是问题,别把它当成指标,盯住内存、CPU才是正经。使用多线程,为的是提高执行效率;那么,是不是线程越多越好呢?

假设我们有100个下载任务,我们可以有以下3种实现方法:

  1. 使用一个线程,依次执行100个下载任务;
  2. 使用100个线程,每个线程执行一个下载任务;
  3. 使用10个线程,每个线程依次执行10个下载任务。

哪种实现方案更好呢?别急!

线程在创建和销毁时的开销:

  • 在创建线程时,系统会分配并初始化一个线程内核对象,并且保留1MB的堆栈空间。Windows还会调用当前进程中的每个DLL的入口函数(参数为DLL_THREAD_ATTACH),通知它们新建了一个线程。
  • 当线程将要被销毁时,Windows也会调用当前进程中的每个DLL的入口函数(参数为DLL_THREAD_DETACH),通知它们该线程即将离去。随后,线程的内核对象以及之前分配的堆栈空间也会被释放。

Windows的做法是,把CPU时间切成小片后再按需分配。Windows会不停地跟踪记录每一个线程对象,大约每隔20毫秒,决定CPU接下来调度哪一个线程使其运行。

对于上图中的三个线程,在CPU中执行的顺序可能是A1B1C1…A2B2C2…A3B3C3… 而在线程A切换到线程B执行时,Windows需要做线程的上下文切换(Context switch),使CPU停止执行当前线程的代码,转而开始执行另一个线程的代码。

上下文切换的过程大致如下(参考文章):

  1. 进入内核模式。
  2. 将CPU的寄存器保存到当前正在执行的线程的内核对象中。
  3. 需要一个自旋锁(spin lock),确定下一次调度哪一个线程,然后再释放该自旋锁。
  4. 如果下一次调度的线程不属于同一个进程,那么此处开销更大,因为OS必须先切换虚拟地址空间。
  5. 把即将要运行的线程的内核对象的地址加载到CPU寄存器中。
  6. 退出内核模式。

在用户模式与内核模式之间切换的系统开销不小,如果线程很多,线程之间频繁切换,开销自然更不可忽视。

对于单核CPU来说,假设在系统中没有运行其他进程的情况下,使用三个线程完成A、B、C三个任务的总时间,与使用一个线程依次完成三个任务的时间其实是相当的;考虑到线程上下文切换的CPU消耗,前者花的时间反而更多。而在多核CPU的情况下,线程可以分散调度到不同的核上,多线程设计能够发挥多核的优势,当然也能显著地提升执行效率。不过,多线程多少算多?依然是个问题。

回顾本文开头那个关于下载的应用场景。显然,只使用一个线程不见得不好。但是,简单粗暴地为每个下载任务都创建一个线程肯定是不足取的!我们应该对线程池做更精致的设计,至于池子里实际使用10个线程还是20个线程,这倒是未必的!

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值