多线程的优缺点及线程池的引入

本篇博客主要讲解多线程的优缺点,以及何时使用线程池
感谢巨人的肩膀
参考资料:UNIX 环境高级编程、雷明:多线程的优缺点、sunflower:多线程的运用和好处
邮箱:[email protected]

简介
  • 典型的 UNIX 进程可以看作只有一个工作线程,CPU 在执行任务时实际上是在调度这些线程(线程是操作系统最小的执行单位),每一个线程都是由创建他的进程所管理,进程为线程分配资源,一个进程中的所有线程共享本进程数据,但每个线程也有自己私有的数据(线程ID、一组寄存器、栈、errno、信号屏蔽字、调度优先级)

  • 举个例子:操作系统相当于地主(CPU 负责调度,进程负责管理线程),进程相当于地主家的管家,而线程可以当作一个一个的苦力,每个管家都有自己的苦力,但每个苦力只属于一个管家管理(相当于一个进程可以创建很多线程,而一个线程只能属于一个进程),管家都是为苦力分配工作,让苦力去执行各种任务,而管家负责为这些苦力提供劳动工具、提供食物、提供住处(进程为线程分配资源,每个线程共享进程的数据),但每个苦力也是人(UNIX 中线程可以看作是一个轻量级进程),也有自己的私有物品(每一个线程都有自己的私有数据)。

多线程的优点:
  • CPU 是以时间片的方式为进程分配 CPU 处理时间的,当一个进程以同步的方式去完成几件事情时,此进程必须完成了第一件事情以后再做第二件事,如此按顺序地向 CPU 请求完成要做的事情。在此单线程的工作模式下,如果把 CPU 当前有 100 个时间片的话,CPU 可能一直都只是用了其中的 10 个时间片来处理当前进程所要做的事情,只是用到了 CPU 的 10% 的时间片,而其他时间都白白浪费了,当然,实际上 CPU 的工作模式还是做完一件事以后再去做另一件事,只是 CUP 的处理速度非常快,很快就处理完成所请求的情事。

  • 为了提高 CPU 的使用率,采用多线程的方式去同时完成几件事情而互不干扰,如当前进程要完成三件事情1、2、3,那么 CPU 会分别用 10% 的时间来同时处理这 3 件事情,从而让 CPU 的使用率达到了 30%,大大地提高了 CPU 的利用率。多线程的好处在处理一些特殊的场合其优势尤其明显。比如下载文件,你要一边下载一边显示进度一边保存,在这种情况下,如果没有用多线程的话,没有意外的话一般都会把主线程阻塞,比如进度条的进度根本没有随着已下载的量而变化,堪至是整个窗体都动不了,用多线程就可以很好地解决这个问题。但使用多线程就可以很好地解决这类问题。

  • 这里还有一个生活实例,当我们做饭时往往需要切菜,煮饭,蒸米……很多情况下我们不用等待一件事做完了再去做下一件事,当我们煮饭时就可以洗菜切菜。这样做大大提升了效率,CPU 花相同时间去完成所有任务,多线程模式可以让 CPU 参插着执行各个线程(因为 CPU 速度很快,在我们看来 CPU 是同时在做很多事情,其实是轮流执行各个线程),所以这就很好的体现了多线程的优点。
    单线程进程多线程应用程序
什么情况下使用
  • 了解了多线程的好处以后,就要了解应该在什么样的情况下使用多线程技术。因为并不是说所有情况下用多线程都是好事,因为多线程的情况下,CPU 还要花时间去维护,CPU 处理各线程的请求时在线程间的切换也要花时间,所以一般情况下是可以不用多线程的,用了有时反而会得不偿失。大多情况下,要用到多线程的主要是需要处理大量的 IO 操作时或者执行某个任务需要花大量的时间等等,比如:读写文件、视频图像的采集、处理、显示、保存等。
总结:
  • 多线程处理模式是一把双刃剑,在使用时需要充分考虑其优缺点,哪些情况需要使用,哪些情况下要避免使用,这是我们需要掌握的重要课题。
    多线程处理方式可以大大提高 CPU 的利用率,多线程程序可以将整个程序划分为多个独立的任务,因此可以在以下几个方面显著提高性能
    优点:

    1. 多线程技术使程序响应速度更快,用户界面可以在执行其他工作的同事一直处于活动状态;

    2. 当前没有需要处理的任务时,可以将处理器时间让给其他任务;

    3. 占用大量处理事件的任务可以定期将处理器时间让给其他任务;

    4. 可以随时停止任务;

    5. 可以分别设置各个任务的优先级以及优化性能;

在这些情况下适合多线程处理:
  1. 好事或大量占用处理器时间的任务阻塞用户界面操作;
  2. 各个任务必须等待外部资源(远程文件传输、服务器处理客户端的链接)
    同样多线程也有很多缺点,在使用多线程时需要进行充分考虑。缺点主要有:
  3. 等候使用公共资源是造成程序运行速度变慢。这些资源主要是独占性资源(打印机,一个线程正在使用,另一个就需要等待)
  4. 对线程管理就需要额外的 CPU 开销。线程的使用会给系统带来上下文切换的额外负担。当这种负担超过一定程度时,多线程的缺点就体现在其特点上,比如用独立的线程更新数组元素。
  5. 线程的死锁。即较长时间的等待或资源竞争以及死锁等多线程症状。
  6. 访问临界资源时(不同线程可共享的数据)。多个线程同时对临界资源进行操作时往往会发生不可预知的情况,,当临界资源的操作是非原子性时,在不同的机器上,中断时间的不确定性,会导致数据在一个线程内的操作产生错误,从而产生莫名其妙的错误,而这种错误是程序员无法预知的。

在分析了多线程模式的优点与缺点的情况下我们可以考虑在多线程模式下引入线程池
面向对象编程中,对象的创建和销毁需要时间,创建对象需要获取内存资源或者其他更多的资源,操作系统需要回收这些资源时也需要时间,特别是一些很好资源的对象需要创建和销毁。如何利用已有对象来服务就是一个需要解决的关键问题,其实就是一些“池化资源”产生的原因。比如 STL 空间适配器,比如数据库连接池,但下面主要介绍线程池。

线程池:
  • 多线程技术主要解决处理器单元内多个线程的执行问题,它可以显著减少处理器单元的闲置时间。举个例子

  • 假设:
    创建一个线程的时间 : T1
    一个线程从接受任务到完成任务的时间为:T2
    销毁一个线程的时间为:T3
    在 T1 + T3 远远大于 T2 时我们使用线程池这种机制,即在实际程序工作时,我们希望尽量减小 T1 和 T3 附加的开销,程序频繁的创建或者销毁线程就必然导致 T1 和 T3 占有非常大的比重,这时我们就可以使用线程池。

    线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1、T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1、T3的开销了,线程池不仅调整T1、T3产生的时间,而且它还显著减少了创建线程的数目。在看一个例子:

  • 线程池是一种多线程处理方法,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程,每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程处于空闲状态,则线程池将会调度一个任务给它,如果所有线程都始终保持繁忙,但将任务放入到一个队列中,则线程池将在一段时间后创建另一个辅助线程,但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

线程池主要有如下几个应用范围:
  • 需要大量的线程来完成任务,且完成任务的时间比较短,如WEB服务器完成网页请求这样的任务。因为单个任务小,而任务数量巨大,比如一个热门网站的点击次数。 但对于长时间的任务,比如一个ftp连接请求,线程池的优点就不明显了。因为ftp会话时间相对于线程的创建时间长多了。

  • 对性能要求苛刻的应用,比如要求服务器迅速相应客户请求。

  • 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限导致系统崩溃。

你可能感兴趣的:(Linux)