前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试问我,创建多少个线程合适?我该怎么说

面试问我,创建多少个线程合适?我该怎么说

原创
作者头像
码农架构
修改2020-10-20 10:55:00
6790
修改2020-10-20 10:55:00
举报
文章被收录于专栏:码农架构码农架构

如果好看,请给个赞

  • 你有一个思想,我有一个思想,我们交换后,一个人就有两个思想
  • If you can NOT explain it simply, you do NOT understand it well enough

为什么要使用多线程?

防止并发编程出错最好的办法就是不写并发程序

既然多线程编程容易出错,为什么它还经久不衰呢?

A:那还用说,肯定在某些方面有特长呗,比如你知道的【它很快,非常快】

我也很赞同这个答案,但说的不够具体

并发编程适用于什么场景?

如果问你选择多线程的原因就是一个【快】字,面试也就不会出那么多幺蛾子了。你有没有问过你自己

  1. 并发编程在所有场景下都是快的吗?
  2. 知道它很快,何为快?怎样度量?

想知道这两个问题的答案,我们需要一个从【定性】到【定量】的分析过程

使用多线程就是在正确的场景下通过设置正确个数的线程来最大化程序的运行速度(我感觉你还是啥也没说)

将这句话翻译到硬件级别就是要充分的利用 CPU 和 I/O 的利用率

两个正确得到保证,也就能达到最大化利用 CPU 和 I/O的目的了。最关键是,如何做到两个【正确】?

在聊具体场景的时候,我们必须要拿出我们的专业性来。送你两个名词 buff 加成

  • CPU 密集型程序
  • I/O 密集型程序

CPU 密集型程序

一个完整请求,I/O操作可以在很短时间内完成, CPU还有很多运算要处理,也就是说 CPU 计算的比例占很大一部分

假如我们要计算 1+2+....100亿 的总和,很明显,这就是一个 CPU 密集型程序

在【单核】CPU下,如果我们创建 4 个线程来分段计算,即:

  1. 线程1计算 [1,25亿)
  2. ...... 以此类推
  3. 线程4计算 [75亿,100亿]

我们来看下图他们会发生什么?

由于是单核 CPU,所有线程都在等待 CPU 时间片。按照理想情况来看,四个线程执行的时间总和与一个线程5独自完成是相等的,实际上我们还忽略了四个线程上下文切换的开销

所以,单核CPU处理CPU密集型程序,这种情况并不太适合使用多线程

此时如果在 4 核CPU下,同样创建四个线程来分段计算,看看会发生什么?

每个线程都有 CPU 来运行,并不会发生等待 CPU 时间片的情况,也没有线程切换的开销。理论情况来看效率提升了 4 倍

所以,如果是多核CPU 处理 CPU 密集型程序,我们完全可以最大化的利用 CPU 核心数,应用并发编程来提高效率

I/O密集型程序

与 CPU 密集型程序相对,一个完整请求,CPU运算操作完成之后还有很多 I/O 操作要做,也就是说 I/O 操作占比很大部分

我们都知道在进行 I/O 操作时,CPU是空闲状态,所以我们要最大化的利用 CPU,不能让其是空闲状态

同样在单核 CPU 的情况下:

从上图中可以看出,每个线程都执行了相同长度的 CPU 耗时和 I/O 耗时,如果你将上面的图多画几个周期,CPU操作耗时固定,将 I/O 操作耗时变为 CPU 耗时的 3 倍,你会发现,CPU又有空闲了,这时你就可以新建线程 4,来继续最大化的利用 CPU。

综上两种情况我们可以做出这样的总结:

线程等待时间所占比例越高,需要越多线程;线程CPU时间所占比例越高,需要越少线程。

到这里,相信你已经知道第一个【正确】使用多线程的场景了,那创建多少个线程是正确的呢?

创建多少个线程合适?

面试如果问到这个问题,这可是对你理论和实践的统考。想完全答对,你必须要【精通/精通/精通】小学算术

从上面知道,我们有 CPU 密集型和 I/O 密集型两个场景,不同的场景当然需要的线程数也就不一样了

CPU 密集型程序创建多少个线程合适?

有些同学早已经发现,对于 CPU 密集型来说,理论上 线程数量 = CPU 核数(逻辑) 就可以了,但是实际上,数量一般会设置为 CPU 核数(逻辑)+ 1, 为什么呢?

《Java并发编程实战》这么说:

计算密(CPU)集型的线程恰好在某时因为发生一个页错误或者因其他原因而暂停,刚好有一个“额外”的线程,可以确保在这种情况下CPU周期不会中断工作。

所以对于CPU密集型程序, CPU 核数(逻辑)+ 1 个线程数是比较好的经验值的原因了

I/O密集型程序创建多少个线程合适?

上面已经让大家按照图多画几个周期(你可以动手将I/O耗时与CPU耗时比例调大,比如6倍或7倍),这样你就会得到一个结论,对于 I/O 密集型程序:

最佳线程数 = (1/CPU利用率) = 1 + (I/O耗时/CPU耗时)

我这么体贴,当然担心有些同学不理解这个公式,我们将上图的比例手动带入到上面的公式中:

这是一个CPU核心的最佳线程数,如果多个核心,那么 I/O 密集型程序的最佳线程数就是:

最佳线程数 = CPU核心数 (1/CPU利用率) = CPU核心数 1 + (I/O耗时/CPU耗时)

说到这,有些同学可能有疑问了,要计算 I/O 密集型程序,是要知道 CPU 利用率的,如果我不知道这些,那要怎样给出一个初始值呢?

按照上面公式,假如几乎全是 I/O耗时,所以纯理论你就可以说是 2N(N=CPU核数),当然也有说 2N + 1的,(我猜这个 1 也是 backup),没有找到具体的推倒过程,在【并发编程实战-8.2章节】截图在此,大家有兴趣的可以自己看看

理论上来说,理论上来说,理论上来说,这样就能达到 CPU 100% 的利用率

如果理论都好用,那就用不着实践了,也就更不会有调优的事出现了。不过在初始阶段,我们确实可以按照这个理论之作为伪标准, 毕竟差也可能不会差太多,这样调优也会更好一些

谈完理论,咱们说点实际的,公式我看懂了(定性阶段结束),但是我有两个疑问:

  1. 我怎么知道具体的 I/O耗时和CPU耗时呢?
  2. 怎么查看CPU利用率?

没错,我们需要定量分析了

幸运的是,我们并不是第一个吃螃蟹的仔儿,其实有很多 APM (Application Performance Manager)工具可以帮我们得到准确的数据,学会使用这类工具,也就可以结合理论,在调优的过程得到更优的线程个数了。我这里简单列举几个,具体使用哪一个,具体应用还需要你自己去调研选择,受篇幅限制,暂不展开讨论了

  1. SkyWalking
  2. CAT
  3. zipkin

上面了解了基本的理论知识,那面试有可能问什么?又可能会以怎样的方式提问呢?

总结

多线程不一定就比但线程高效,比如大名鼎鼎的 Redis (后面会分析),因为它是基于内存操作,这种情况下,单线程可以很高效的利用CPU。而多线程的使用场景一般时存在相当比例的I/O或网络操作

另外,结合小学数学题,我们已经了解了如何从定性到定量的分析的过程,在开始没有任何数据之前,我们可以使用上文提到的经验值作为一个伪标准,其次就是结合实际来逐步的调优(综合 CPU,内存,硬盘读写速度,网络状况等)了

最后,盲目的增加 CPU 核数也不一定能解决我们的问题,这就要求我们严格的编写并发程序代码了

灵魂追问

  1. 我们已经知道创建多少个线程合适了,为什么还要搞一个线程池出来?
  2. 创建一个线程都要做哪些事情?为什么说频繁的创建线程开销很大?
  3. 多线程通常要注意共享变量问题,为什么局部变量就没有线程安全问题呢?
  4. ......

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么要使用多线程?
  • 并发编程适用于什么场景?
    • CPU 密集型程序
      • I/O密集型程序
      • 创建多少个线程合适?
        • CPU 密集型程序创建多少个线程合适?
          • I/O密集型程序创建多少个线程合适?
          • 总结
          • 灵魂追问
          相关产品与服务
          云数据库 Redis
          腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档