分类: LINUX
2012-01-31 22:02:51
++++++APUE读书笔记-11线程(1)++++++
1、简介
================================================
我们前面讨论了进程,知道了unix的进程环境,进程之间的关系以及控制进程的方法。我们可以看到,进程间可以进程有限的共享。
本章,我们将会深入到进程的内部,来看看我们如何在单进程环境中使用多线程控制。所有同一个进程中的线程都可以共享访问例如文件描述符号,内存等进程资源。
任何时候,你想要在多个用户之间共享单个资源的时候,你都需要处理一致性的问题。我们后面引入了线程的同步机制,防止在它们之间出现共享的资源不一致的情况。
参考:
2、线程的概念
================================================
一个典型的UNIX进程,可以被认为是单线程控制的:每个进程每个时刻只做一件事情。在多线程控制中,我们可以让自己的程序每次做更多的事情,每个线程处理一个任务,这样的方法有许多的好处:
(a)我们可以简化可以通过指定一个线程来处理每个事件,来简化对异步事件处理的代码。每个线程之后可以使用一个同步的编程模型来处理它的事件。同步编程模型要比异步简单的多。
(b)多进程需要使用操作系统提供的复杂的机制来共享内存和文件描述符号。而多线程的化,可以直接访问同一个进程中的同一个内存地址和文件描述符号。
(c)有些问题可以被分割,这样整体的程序吞吐量会提升。而单个进程处理多个任务的话会隐式地将这些任务串行化,因为只有一个线程控制。在多线程控制中,每个线程可以处理独立的任务,独立的任务可以交叉地执行,所谓独立的任务就是它们之间不会相互依赖。
(d)类似地,交互程序通过使用多线程技术,可以提升用户的响应时间,主要是把和用户输入输出交互的部分和程序的其它部分别用不同的线程处理。
有些用户把多线程和多处理器联系起来。实际多线程带来的好处即使是在单个的cpu系统中也是存在的。一个程序可以通过多线程被简化,而不必考虑处理器的数目,因为处理器的数目不会影响程序的结构。另外,只要你的程序在串行任务中被阻塞,你就可能可以通过多线程提高程序吞吐,因为线程在其它线程阻塞的时候还是可以运行的。
在进程中,一个线程包含了代表一个执行单元的必要的信息,这些信息包含:线程ID(用来标识一个进程中的线程),一系列寄存器的值,一个堆栈,调度优先级和策略,一个signal mask,和errno变量,还有线程相关的数据。所有在一个进程中的东西在线程中都是可以被共享的,包含程序的可执行代码,程序的全局变量和堆内存,堆栈,和文件描述符号。
我们将要看的线程接口来自POSIX.1-2001.这些线程的接口也被称作“pthreads”(POSIX threads),是POSIX.1-2001中的一个可选的部分。这个特性,可以使用_POSIX_THREADS宏来进行测试。应用程序可以在编译的时候使用#ifdef来测试是否支持线程,也可以在运行的时候使用sysconf的_SC_THREADS常量来确定是否支持线程。
参考:
3、线程标识符号
================================================
就像每个进程都有一个进程ID,每个线程都有线程ID。和进程ID不一样,进程ID在系统中是唯一的;线程ID只有在它所在的进程的上下文中才有意义。
需要记住的是,进程ID使用pid_t类型来表示,并且它是一个非负的整数。线程ID用pthread_t数据类型来代替,有些实现允许使用一个数据结构来代表pthread_t数据类型,所以可移植的实现不允许把它们做为整数来看待。所以有一个专门用来比较thread ID的函数。
#include
int pthread_equal(pthread_t tid1, pthread_t tid2);
返回:如果相等返回非0,如果不等返回0。
Linux2.4.22使用无符号长整数代表pthread_t数据类型。Solaris 9使用pthread_t数据类型是unsigned int. FreeBSD 5.2.1和Mac OS X 10.3使用一个指向pthread数据结构的指针来表示pthread_t数据类型。
允许pthread_t数据类型是一个结构,这样会导致无法使用一个比较容易移植的方法来打印它的值。有时,在调试程序的时候打印线程ID是很重要的,但是其他时候,一般来说也没有必要非得这么做.最差的时候,会得到一个不可移植的调试程序的代码,所以这也不是一个不能接受的限制。
线程可以通过调用thread_self函数来获得它自己的线程ID。
#include
pthread_t pthread_self(void);
返回:调用线程的thread ID。
这个函数可以和pthread_equal一块使用,来辨别一个数据结构是否是它自己的thread ID.例如一个主线程可能会把工作分配到一个队列上面,使用thread ID来判断那个作业被那个工作线程处理。
这里给出了一个图示,展示工作队列:
+--------+
| master |
/| thread |
/ +--------+
+-------+ v
|thread1|<----\ +------+
+-------+ \ | | +------+ +------+ +------+ +------+
\| | | TID1 | | TID3 | | TID2 | | TID3 |
+-------+ | work | +------+ +------+ +------+ +------+
|thread2|<----------|queue |<--->| |<--->| |<--->| |<--->| |
+-------+ | | | job | | job | | job | | job |
/| | | | | | | | | |
+-------+ / | | +------+ +------+ +------+ +------+
|thread3|<----/ +------+
+-------+
图示中,一个单个的主线程将新的作业放到工作队列中,有三个工作线程会把作业从队列中移走。为了可以让每个线程处理队头的作业,主线程在每个作业结构中添加了一个thread ID成员来表示应该处理这个作业的线程,每个线程只会从工作队列中移走它对应的线程ID的工作。
参考: