[原]多线程多少算多?

标签: | 发表时间:2016-10-03 08:56 | 作者:happydeer
出处:http://blog.csdn.net/happydeer

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

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

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

哪种实现方案更好呢?别急!我们先来理一理两个基本概念:进程与线程。我发现 这篇文章总结得言简意赅:

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

……

简而言之,一个程序至少有一个进程,一个进程至少有一个线程……对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程…

然而,使用线程是有成本的。我们先来看一下线程在创建和销毁时的开销:

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

再来看线程的执行过程。使用多线程可以实现多个任务的并行。这个很棒!但我们还需要理解Windows是怎么来实现多任务并行的。我们已经知道,线程是CPU调度的基本单位;对于单核的CPU来说,在某一时刻只有一个线程可以运行。系统怎么让所有线程雨露均沾呢?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. 退出内核模式。

在用户模式与内核模式之间切换的系统开销不小,如果线程很多,线程之间频繁切换,开销自然更不可忽视。线程的整个生命周期对于系统来说都是一种负担。因此,线程不是越多越好!线程的数量必须是有节制的。每增加一个线程,我们都需要去评估ROI,力求平衡!对于单核CPU来说,假设在系统中没有运行其他进程的情况下,使用多线程完成A、B、C三个任务的总时间,与使用一个线程依次完成三个任务的时间其实是相当的;考虑到线程上下文切换的CPU消耗,前者花的时间反而更多。而在多核CPU的情况下,线程可以分散调度到不同的核上,多线程设计能够发挥多核的优势,当然也能显著地提升执行效率。不过,多线程多少算多?依然是个问题。

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

作者:happydeer 发表于2016/10/3 0:56:41 原文链接
阅读:23 评论:0 查看评论

相关 [多线程] 推荐:

Java Thread多线程

- - CSDN博客推荐文章
Java Thread多线程. Java 多线程例子1 小例子. super("zhuyong");//设置线程的名字,默认为“TestThread”. Java 多线程例子2 前台线程(用户线程) 后台线程(守护线程 ). 1,setDaemon(true)后就是后台线程(守护线程 ),反之就是前台线程(用户线程).

Java多线程之synchronized

- - CSDN博客推荐文章
这里通过三个测试类阐述了synchronized应用的不同场景. 首先是最基本的synchronized Method的使用.  * @see 概述:Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor) .  * @see 说明:当synchronized关键字修饰一个方法时,则该方法为同步方法 .

[转]GDB调试多线程

- - 小彰
GDB 多线程调试基本命令 实现简介 以及一个问题的解决. 一直对GDB多线程调试接触不多,最近因为工作有了一些接触,简单作点记录吧. 先介绍一下GDB多线程调试的基本命令. 显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID. 切换当前调试的线程为指定ID的线程.

java多线程总结

- - Java - 编程语言 - ITeye博客
在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口. 对于直接继承Thread的类来说,代码大致框架是:. class 类名 extends Thread{. * @author Rollen-Holt 继承Thread类,直接调用run方法.             System.out.println(name + "运行     " + i);.

[转]Oracle Parallel 多线程

- - Oracle - 数据库 - ITeye博客
对于一个大的任务,一般的做法是利用一个进程,串行的执行,如果系统资源足够,可以采用parallel技术,把一个大的任务分成若干个小的任务,同时启用n个进程/线程,并行的处理这些小的任务,这些并发的进程称为并行执行服务器(parallel executeion server),这些并发进程由一个称为并发协调进程的进程来管理.

Java多线程学习

- - CSDN博客编程语言推荐文章
  线程是一种轻量级的进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单. 即多个线程可以同时执行,就像有多条流水线一样,可以同时进行工作,是并发执行的.   程序是由进程组成的,进程是由线程组成的.

Java多线程(二)同步

- - CSDN博客编程语言推荐文章
如果你的java基础较弱,或者不大了解java多线程请先看这篇文章 java多线程(一)线程定义、状态和属性. 同步一直是java多线程的难点,在我们做android开发时也很少应用,但这并不是我们不熟悉同步的理由. 希望这篇文章能使更多的人能够了解并且应用java的同步. 在多线程的应用中,两个或者两个以上的线程需要共享对同一个数据的存取.

Lua 下实现抢占式多线程

- Coder(码农) - 云风的 BLOG
Lua 5.2 的开发进度可以回溯到 2010 年 1 月. 漫长的流程到今天已经快两年过去,终于等到了 beta 版. 我十分期待它可以在 2011 年内正式发布. 在这几经折腾的两年里,许多新特性企图挤进 5.2 版,又最终被否决. 当我们审视改进列表,似乎看不到太多耳目一新的东西. 但如果仔细阅读一下源代码,就会发现,大部分地方都重新实现过了,以配合这些表面上看起来不大的修改.

浅析C++多线程内存模型

- 2sin18 - 并行实验室 | Parallel Labs
注:本文发表于《程序员》2011年第6期并行编程专栏,略有删改. 在即将到来的C++1x标准中,一个重大的更新就是引入了C++多线程内存模型. 本文的主要目的在于介绍C++多线程内存模型涉及到的一些原理和概念,以帮助大家理解C++多线程内存模型的作用和意义. 顺序一致性模型(Sequential Consistency).

Android实战技巧:多线程AsyncTask

- - CSDN博客推荐文章
AsyncTask是Android 1.5 Cubake加入的用于实现异步操作的一个类,在此之前只能用Java SE库中的Thread来实现多线程异步,AsyncTask是Android平台自己的异步工具,融入了Android平台的特性,让异步操作更加的安全,方便和实用. 实质上它也是对Java SE库中Thread的一个封装,加上了平台相关的特性,所以对于所有的多线程异步都强烈推荐使用AsyncTask,因为它考虑,也融入了Android平台的特性,更加的安全和高效.