分类:
2011-10-14 13:19:11
原文地址:Thread──多线程编程 作者:guobutter
|
占用资源 |
通信机制 |
进程 |
独立的地址空间,代码段,堆栈段,数据段 |
通信的方式实现 |
线程 |
进程中的多线程使用相同的地址空间,共享大部分数据 |
共享数据(编程时注意共享数据的保护) |
线程的其他优点:
1)程序的响应性好:将耗时长的操作置于一个新的线程,提高系统响应性
2)提高多CPU性能:不同的线程运行与不同的CPU上
3)改善程序结构:将复杂进程分解成多个线程
二 函数
1) extern int pthread_create __P((pthread_t *__thread, __const pthread_attr_t *__attr, void *(*__start_routine) (void *), void *__arg));
函数描述:创建一个线程,成功返回0。创建成功后,运行参数三和参数四确定的函数,原来的线程则继续 运行下一行代码。
错误返回码: EAGAIN:线程数目过多
EINVAL:属性值非法
参数:
pthread_t *__thread: 线程标识符指针
__const pthread_attr_t *__attr: 线程属性
void *(*__start_routine) (void *): 线程运行函数的起始地址
void *__arg: 运行函数的参数
2) extern int pthread_join __P((pthread_t __th, void ** __pthread_return))
函数描述:等待一个线程的结束。调用他的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被回收。
参数:
pthread_t __th: 被等待线程的标识符
void **__pthread_return : 用户定义指针,可以用来存放被等待线程的返回值
3)extern void pthread_exit __P((void *__retval) __attribute__((__noreturn__));
函数描述:强迫线程退出。
参数:
__noreturn__:函数的返回代码。只要pthread_join中第二个参数thread_return不是NULL,这个值将被传递给thread_return
三线程属性(pthread_attr_t)/*/usr/include/pthread.h*/
属性对象:是否绑定、是否分离、堆栈地址、堆栈大小、优先级
默认属性:非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。
1)
轻进程(LWP:Light Weight Process):可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程。
非绑定(PTHREAD_SCOPE_PROCESS):默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的。
被绑定(PTHREAD_SCOPE_SYSTEM):某个线程固定的"绑"在一个轻进程之上。被绑定的线程具有较高的响应速度(CPU时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个轻进程可用)。
设置函数: pthread_attr_setscope(属性结构体指针,绑定类型);
2)线程的分离状态决定线程以什么样的方式来终止自己。
非分离(PTHREAD_CREATE_JOINABLE):原有的线程等待创建的线程结束。只有当pthread_join()函数返回了,创建的线程才算终止,才能释放自己占有的资源。
分离(PTHREAD_CREATE_DETACHED):没有被其他线程等待,自己运行结束,也就终止,马上释放系统资源。
设置函数:pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
存在问题:分离线程可能在pthread_create()返回之前就终止,若该分离线程的线程号和系统资源移交给其他线程,pthread_create()就得到了错误的线程号。
解决:在被调用的线程里调用pthread_cond_timewait(),等待pthread_create()返回。
附:不使用wait()之类的函数,它们使整个进程睡眠,并不能解决线程同步问题。
3)线程优先级(在sched_param中)
函数: pthread_attr_getschedparam(&attr, ¶m); //取attr的优先级
pthread_attr_setschedparam(&attr, ¶m); //设置attr的优先级
四线程的数据处理
线程最大的优点之一就是数据共享,进程中共享的变量必须用关键字volitile来定义。为了保护变量,信号量、互斥等方法。
1)线程数据
单线程基本数据:全局变量和局部变量
多线程数据:全局变量、局部变量和线程数据(TSD: Thread-Specific Date)。
TSD:在线程内部,各个函数可以象使用全局变量一样调用它,但它对线程外部的其它线程是不可见的。每个线程数据和一个键相关联,在各个线程里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的。
函数:
a. 创建一个键
extern int pthread_key_create __P((pthread_key_t *__key, void (*__destr_function)(void *)))
第一个参数:键值指针
第二个参数:指明一个destructor函数,非空时,在线程结束时调用这个函数释放绑定在这个键上的内存块。
常和函数pthread_once ((pthread_once_t*once_control, void (*initroutine) (void)))一起使用,为了让这个键只被创建一次。
函数pthread_once声明一个初始化函数,第一次调用pthread_once时它执行这个函数,以后的调用将被它忽略。
b. 为一个键绑定一个线程数据
extern int pthread_setspecific __P((pthread_key_t __key, __const void *__pointer));
注:分配新线程数据时,必须自己释放原有的线程数据以回收空间
c. 读取一个键的线程数据
extern void *pthread_getspecific __P((pthread_key_t __key));
d. 删除一个键
extern void pthread_key_delete(pthread_key_t __key)
注:只释放键占有的内存,并不释放该建关联的线程数据所占用的内存资源,而且它也不会触发函数pthread_key_create中定义的destructor函数。线程数据的释放必须在释放键之前完成。
2) 互斥锁
保证一段时间内只有一个线程在执行一段代码。
结构:pthread_mutex_t,不公开数据类型,包含一个系统分配的属性对象。
生成函数:
pthread_mutex_init(&mutex, NULL),NULL表明使用默认属性
设置属性:
pthread_mutex_setpshared(),设置属性shared:
PTHREAD_PROCESS_PRIVATE:不同进程中的线程的同步,为默认值
PTHREAD_PROCESS_SHARED:本进程的不同线程的同步
pthread_mutex_settype(),设置互斥锁类型
PTHREAD_MUTEX_NORMAL:
PTHREAD_MUTEX_ERRORCHECK:
PTHREAD_MUTEX_RECURSIVE:
PTHREAD_MUTEX_DEFAULT:默认类型
pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,均被上锁,即同一时间只能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一个线程释放此互斥锁。
pthread_mutex_trylock,是函数pthread_mutex_lock的非阻塞版本,当它发现死锁不可避免时,它会返回相应的信息,程序员可以针对死锁做出相应的处理。另外不同的互斥锁类型对死锁的处理不一样,但最主要的还是要程序员自己在程序设计注意这一点。
3)条件变量
互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步。
结构:pthread_cond_t
初始化函数:
extern int pthread_cond_init __P((pthread_cond_t *__cond, __const pthread_condattr_t *__cond_attr));
pthread_condattr_t:条件变量属性结构体,设置条件变量是进程内还是进程间。默认值是PTHREAD_PROCESS_PRIVATE,即进程内使用。
pthread_cond_destroy(pthread_cond_t cond):释放条件变量。条件变量只有在未被使用时才能被重新初始化或是被释放。
线程阻塞:
extern int pthread_cond_wait __P((pthread_cond_t *__cond, pthread_mutex_t *__mutex));
extern int pthread_cond_timedwait __P((pthread_cond_t *__cond, pthread_mutex_t *__mutex, __const struct timespec *__abstime)): 经历abstime时间后,条件变量不满足,阻塞也被解除。
线程唤醒:
extern int pthread_cond_signal __P((pthread_cond_t *__cond)):多个线程阻塞在此条件变量上时,哪一个线程被唤醒是由线程的调度策略所决定的。要注意的是,必须用保护条件变量的互斥锁来保护这个函数,否则条件满足信号又可能在测试条件和调用pthread_cond_wait函数之间被发出,从而造成无限制的等待。
extern int pthread_cond_broadcast __P((pthread_cond_t *__cond)):唤醒所有阻塞线程,唤醒后的线程再次竞争相应的互斥锁。
4)信号量(/usr/include/semaphore.h)
信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。
结构:sem_t,一个长整型数
初始化:
extern int sem_init __P((sem_t *__sem, int __pshared, unsigned int __value));
sem:信号量结构体指针
pshared:非0, 在进程间共享,否则,在当前进程所有线程间共享
value:信号量的初始值
sem_post(sem_t *sem)增加信号量值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。
sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。
sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。
sem_destroy(sem_t *sem)用来释放信号量sem。