分类: C/C++
2016-11-29 11:34:00
原文地址:多线程编程-特点与分类 作者:zhenhuaqin
一.定义多线程术语:
1.基本术语:
线程:在进程的内部执行的指令序列;
单 单线程:单线程;
多线程:多线程;
用户级线程:在用户空间内的由线程函数库进程控制的现成;
轻进程:又称LWP,内核内部的执行核代码和系统调用的线程;
绑定(bound)线程:永远限制在LWP内的线程;
非绑定(unbound)线程:在LWP动态捆绑和卸绑的线程;
记数信号量:一个基于内存的同步机制;
2.定义同时(concurrency)和并行(parallism):
在进程内至少同时有两个线程进行(process)时存在同时性问题;至少
同时有两个线程在执行时存在并行问题;
在单处理器上执行的多线程的进程内部,处理器可以在线程中间切换执
行,这样实现了同时执行;在共享内存多处理器上执行的同一个多线程进程,
每一个线程可以分别在不同的处理器上进行,是为并行。
当进程里的线程数不多于处理器的数量时,线程支持系统和操作系统保
证线程在不同的处理器上执行。例如在一个m处理器和m线程运行一个矩阵乘
法,每一个线程计算一列。
二.线程的优缺点:
1.优点:
多线程处理可以同时运行多个线程。由于多线程应用程序将程序划分成多个独立的任务,因此可以在以下方面显著提高性能:
1.1提高应用程序响应
任何一个包含很多互不关联的操作(activity)的程序都可以被重新设
计,使得每一个操作成为一个线程。例如,在一个GUI(图形用户界面)内执
行一个操作的同时启动另外一个,就可以用多线程改善性能。
1.2使多处理器效率更高
典型情况下,有同时性需求的多线程应用程序不需要考虑处理器的数量。
应用程序的性能在被多处理器改善的同时对用户是透明的。
数学计算和有高度并发性需求的应用程序,比如矩阵乘法,在多处理器平
台上可以用多线程来提高速度。
1.3改善程序结构
许多应用程序可以从一个单一的、巨大的线程改造成一些独立或半独立的
执行部分,从而得到更有效的运行。多线程程序比单线程程序更能适应用户需
求的变更。
1.4占用较少的系统资源
应用程序可以通过使用两个或更多的进程共享内存的办法来实现多于一个
现成的控制。然而,每一个进程都要有一个完整的地址空间和操作系统状态表
项。用于创建和维护多进程大量的状态表的开销与多线程方法相比,在时间上
和空间上都更为昂贵。而且,进程所固有的独立性使得程序员花费很多精力来
实现进程间的通信和同步。
1.5把线程和RPC结合起来
把多线程和RPC(remote procedure call,远程过程调用)结合起来,
你可以使用没内存共享的多处理器(比方说一个工作站组)。这种结构把这组
工作站当作一个大的多处理器系统,使应用程序分布得更加容易。
例如,一个线程可以创建子线程,每一个子进程可以做RPC,调用另外一
台机器上的过程。尽管最早的线程仅仅创建一些并行的线程,这种并行可以包
括多台机器的运行。
1.6 可以分别设置各个任务的优先级以优化性能,如线程创建时间和线程同步时间的改善。s
2.多线程的主要缺点包括:
(1)等候使用共享资源时造成程序的运行速度变慢。这些共享资源主要是独占性的资源 ,如打印机等。
(2)对线程进行管理要求额外的 CPU开销。线程的使用会给系统带来上下文切换的额外负担。当这种负担超过一定程度时,多线程的特点主要表现在其缺点上,比如用独立的线程来更新数组内每个元素。
(3)线程的死锁。即较长时间的等待或资源竞争以及死锁等多线程症状。
(4)对公有变量的同时读或写。当多个线程需要对公有变量进行写操作时,后一个线程往往会修改掉前一个线程存放的数据,从而使前一个线程的参数被修改;另外 ,当公用变量的读写操作是非原子性时,在不同的机器上,中断时间的不确定性,会导致数据在一个线程内的操作产生错误,从而产生莫名其妙的错误,而这种错误是程序员无法预知的。
是否需要创建多个线程取决于各种因素。在以下情况下,最适合采用多线程处理:
(1)耗时或大量占用处理器的任务阻塞用户界面操作;
(2)各个任务必须等待外部资源 (如远程文件或 Internet连接)。
三.线程的结构或分类
在多线程操作系统中,各个系统的实现方式并不相同。在有的系统中实现了用户级线程,有的系统中实现了内核级线程
1.内核级线程:
(1)线程的创建、撤销和切换等,都需要内核直接实现,即内核了解每一个作为可调度实体的线程。
(2)这些线程可以在全系统内进行资源的竞争。
(3)内核空间内为每一个内核支持线程设置了一个线程控制块(TCB),内核根据该控制块,感知线程的存在,并进行控制。
在一定程度上类似于进程,只是创建、调度的开销要比进程小,有的统计是1:10
内核线程的优点:
(1)当有多个处理机时,一个进程的多个线程可以同时执行
缺点:(1)由内核进行调度
2.用户级线程:
(1)用户级线程仅存在于用户空间。——>对比内核(3)
(2)内核并不能看到用户线程。——>重要的区别
(3)内核资源的分配仍然是按照进程进行分配的;各个用户线程只能在进程内进行资源竞争。
(4) 每一个线程的下列状态在进程内部是惟一的:
线程号;寄存器状态(包括程序计数器和堆栈指针);堆栈;信号掩码;优先级;线程私有的存储段。
用户线程运行在一个中间系统上面。目前中间系统实现的方式有两种,即运行时系统(Runtime System)和内核控制线程。
“运行时系统”实质上时用于管理和控制线程的函数集合。包括创建、撤销、线程的同步和通信的函数以及调度的函数。这些函数都驻留在用户空间作为用户线程和内核之间的接口。用户线程不能使用系统调用,而是当线程需要系统资源时,将请求传送给运行时,由后者通过相应的系统调用来获取系统资源。
内核控制线程:
系统在分给进程几个轻型进程(LWP),LWP可以通过系统调用来获得内核提供的服务,而进程中的用户线程可通过复用来关联到LWP,从而得到内核的服务。
用户进程的优点:(1)线程的调度不需要内核直接参与,控制简单
缺点:(1)资源调度按照进程进行,多个处理机下,同一个进程中的线程只能在同一个处理机下分时复用。
3 非绑定线程Unbound Threads
在LWP缓冲池中排队的线程称为unbound thread。通常情况下我们的线程
都是unbound的,这样他们可以在LWP之间自由切换。
线程库在需要的时候激活LWP并把它们交给可以执行的线程。LWP管理线程
的状态,执行线程的指令。如果线程在同步机制中被阻塞,或者其他线程需要
运行,线程状态被存在进程内存中,LWP被移交给其他线程。
4 绑定线程Bound Threads
如果需要,你可以将一个线程绑定在某个LWP上。
例如,你可以通过绑定一个线程来实现:
1. 将线程全局调度(例如实时)
2. 使线程拥有可变的信号栈
3. 给线程分配独立的定时器和信号(alarm)
在线程数多于LWP时,bounded比unbound线程体现出一些优越性。
例如,一个并行的矩阵计算程序在每个线程当中计算每一行。如果每个处
理器都有一个LWP,但每个LWP都要处理多线程,每个处理器将要花费相当的时间来切换线程。在这种情况下,最好使每个LWP处理一个线程,减少线程数,
从而减少线程切换。
在一些应用程序中,混合使用bound和unbound线程更加合适。
例如,有一个实时的应用程序,希望某些线程拥有全局性的优先级,并被
实时调度,其他线程则转入后台计算。另一个例子是窗口系统,大多数操作都
是unbound的,但鼠标操作需要占用一个高优先级的,bound的,实时的线程。
四.多线程的标准
多线程编程的历史可以回溯到二十世纪60年代。在UNIX操作系统中的发展是从80年代中期开始的。也许是令人吃惊的,关于支持多线程有很好的协议,
但是今天我们仍然可以看到不同的多线程开发包,他们拥有不同的接口。
但是,某几年里一个叫做POSIX
准完成后,大多数支持多线程的系统都支持POSIX接口。很好的改善了多线程编程的可移植性。