一、线程的创建和退出
1.函数原型
#include <pthread.h>
int pthread_create(pthread_t* thread,pthread_attr_t* attr,void *(*start_routine)(void*),void *arg);
void pthread_exit(void *retval);
pthread_t pthread_self(void);
通用形式
pthread_t pthid;
int pthread_create(&pthid,NULL,pthFunc,NULL)或pthread_create(&pthid,NULL,pthFunc,(void*)3);
pthread_exit(NULL)或pthread_exit((void*)3);//3作为返回值被pthread_join函数扑获
2.各参数描述
参数thread是传出参数,保存新进程的标识
参数attr是一个结构体指针,结构体中的元素分别指定新线程的运行属性
参数start_routine是一个函数指针,指向新线程的入口点函数,线程入口函数带有一个void*的参数,由pthread_create的第四个参数传入
参数arg用于传递给第三个参数指向的入口点函数的参数
函数pthread_exit表示线程的退出,其参数可以被其他线程用pthread_join函数捕获
二、线程的等待退出
1.等待线程退出
int pthread_join(pthread_t th,void **thread_return);
(1)该函数是一个阻塞函数,一直等到参数th指定的线程返回。thread_return是一个传出参数,接收线程函数的返回值。如果线程通过调用pthread exit()终止,则该函数内的参数相当于自然返回值,照样可以被其他线程用pthread_join获得。
(2)th传递0值时,join返回ESRCH错误
(3)易错点 pthread_join(pthid,(void**)&iRet);
(4)该函数还有一个非常重要的作用,由于一个进程的多个线程共享数据段,因此通常在一个线程退出后,退出线程所占的资源并不会随线程的结束而释放。如果线程类型不是自动清理资源类型的,则线程退出后,线程本身的资源必须通过其他线程调用pthread_join来清除。
(5)该函数不回收堆内存的,只回收线程的栈内存和内核中的struct task_struct结构占用的
2.线程的取消
int pthread_cancel(pthread_t thread);
(1)线程取消的方法是一个线程向目标线程发cancel信号,但是如何处理cancel信号则由目标线程来决定,目标线程或者忽略,或者立即终止,或者继续运行到取消点后终止
(2)取消点(camcelation-point)
pthread_join()
pthread_cond_wait();
sem_wait();
sigwait();
read();
write();
printf();
getchar();
sleep();
......
3.线程终止清理函数
void pthread_cleanup_push(void (*routine)(void*),void *arg)
void pthread_cleanup_pop(int execute);
(1)清理函数采用先入后出的栈结构管理
(2)void routine(void *arg)函数在调用pthread_cleanup_push()时压入清理函数栈。必须成对出现
(3)pthread_cleanup_pop的参数如果为非0值时,则按照栈的顺序注销掉一个原来注册的清理函数,并执行该函数;如果参数为0时,仅仅在pthread_exit函数或者其他线程对本线程调用pthread_cancel函数时,才弹出“清理函数”的同时执行“清理函数”。
三、线程的同步和互斥
1.线程的互斥
(1)创建与销毁锁
以静态方式:定义宏来静态初始化互斥锁
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
以动态方式:采用pthread_nutex_init()函数来初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *mutexattr)
其中mutexattr是指互斥锁属性,通常为NULL;
注销一个互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
销毁一个互斥锁即意味着释放它所占的资源,且要求锁处于开放状态 。检查锁的状态,如果是锁定状态则返回EBUSY
初始化一个快速锁:
pthread_mutex_t lock;
pthread_mutex_init(&lock,NULL);
(2)互斥锁的属性设置
PTHREAD_MUTEX_TIMED_NP (NULL)
PTHREAD_MUTEX_RECURSIVE (允许一个线程对同一个锁成功获得多次,并通过多次unlock解锁)(递归锁)
pthread_mutex_errorcheck (检错锁)
示例:
pthread_mutex_t lock;
pthread_mutexattr_t mutexattr;
pthread_mutexattr_settype(&mutexattr,PTHREAD_MUTEX_ERRORCHECK);
pthread_mutex_init(&lock,&mutexattr);
(3)锁操作
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
加锁操作时,不论是哪种类型的锁,都不能被两个不同的线程同时得到,而必须等待解锁。对于普通锁,解锁者可以是同进程的任何线程;而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由加锁者解锁。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法获得锁
解锁操作时,对于快速锁,该函数解除锁定;对于递归锁,该函数使锁上的引用计数减一;对于检错锁,如果锁是当前线程锁定时,则解除锁定,否则什么也不做
测试加锁,与加锁操作不同的是在锁已经占据时返回EBUSY而不是挂起等待
(4)注意
如果线程在加锁后解锁前被取消,锁将永远保持锁的状态,因此如果在关键区内有取消点在,则必须在退出回调函数pthread_cleanup_push/pthread_cleanup_pop中解锁,同时不应该在信号处理函数中使用互斥锁
2.线程的同步
(1)条件变量:利用线程间共享的全局变量进行同步的一种机制,主要有两个动作,一是线程等待条件变量的条件成立而挂起 ,二是线程使条件成立。条件变量的使用总是和一个互斥锁在一起
(2)创建 条件变量 动态
int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *condattr);
int pthread_cond_destroy(pthread_cond_t *cond);
注销一个条件变量时,只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY
(3)等待和激发
等待条件有两种方式:无条件等待和计时等待
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex_t *mutex,const struct timespec *abstime);
线程解开mutex指向的锁并被条件变量cond阻塞。
激发条件有两种形式:
pthread_cond_signal();
pthread_cond_broadcast();