分类: LINUX
2010-12-06 13:27:53
简介
线程(thread),有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈级成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程有新建、就绪、运行、阻塞和死亡几种基本状态。
线程和进程
线程和进程的区别在于,子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度,从而显著提高系统资源的利用率和吞吐量。因而近年来推出的通用操作系统都引入了线程,以便进一步提高系统的并发性,并把它视为现代操作系统的一个重要指标。
线程的属性
在介绍线程之前,有必要了解一下linux中线程的属性,该属性在创建线程时由创建函数提供。linux线程属性主要包括邦定属性、分离属性、堆栈地址、堆栈大小、优先级。其中系统默认的是非邦定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。
(1) 绑定属性
在LINUX中,采用的是“一对一”的线程机制。也就是一个用户线程对应一个内核线程。邦定属性就是指一个用户线程固定地分配给一个内核线程,因为CPU时间片的调度是面向内核线程(轻量级进程)的,因此具有邦定属性的线程可以保证在需要的时候总有一个内核线程与之对应,而与之对应的非邦定属性就是指用户线程和内核线程的关系不是始终固定的,而是由系统来分配。
(2)分离属性
分离属性是决定以一个什么样的方式来终止自己。在非分离情况下,当一个线程结束时,它多占用的线程没有得到释放,也就是没有真正的终止,需要通过pthread_join来释放资源。而在分离属性情况下,一个线程结束时会立即释放它所占有的系统资源。这里有一点要注意,如果设置一个线程为分离属性,而这个线程又运行得非常快的话,那么它很可能在pthread_create函数返回之前线程函数就已经终止,它终止以后就很有可能将线程号和系统资源移交给其他的线程使用,这时调用pthread_create的线程就得到错误的线程号。
这些属性都是通过一些函数来完成的,通常先调用pthread_attr_init来初始化,之后来调用相应的属性设置函数。见下面。
线程的创建、运行及退出
本文线程的操作都是用户级的操作。在LINUX中,有一套通用的线程库pthread。
(1)创建线程函数:
int pthread_create (pthread_t* thread,pthread_attr_t* attr,void* (start_routine)(void*),void* arg);
参数:thread: 线程标识符
attr:线程属性设置,如果属性为NULL,将采用默认的属性配置
start_routine 线程函数入口
arg 传递给线程函数的唯一参数
返回值:0: 成功,-1: 失败
(2)线程的执行函数:
void* (start_routine)(void*),该函数是一个自定义函数,有一个void*参数,该参数由pthread_create的arg提供。
(3)线程的退出函数:
void pthread_exit (void* retval);
参数:retval:为其它线程函数如pthread_join的thread_return提供返回结果。
(4)等待非分离线程及释放其资源函数:
int pthread_join (pthread_t* thread, void** thread_return);
参数:thread:等待线程的标识符;
thread_return:用户定义的指针,用来存储被等待线程的返回值;
返回值:成功: 0 失败:-1
线程的属性设置
前面讲到一些线程的属性,这些属性都是通过一些函数来完成的,通常先调用pthread_attr_init来初始化,之后来调用相应的属性设置函数。
1、pthread_attr_init
功能:对线程属性变量的初始化。
int pthread_attr_init (pthread_attr_t* attr);
参数:attr:线程属性。
返回值:成功: 0 失败: -1
2、pthread_attr_setscope
功能:设置线程绑定属性。
int pthread_attr_setscope (pthread_attr_t* attr, int scope);
参数:attr: 线程属性。
scope:PTHREAD_SCOPE_SYSTEM(绑定)
PTHREAD_SCOPE_PROCESS(非绑定)
返回值:成功: 0 失败: -1
3、pthread_attr_setdetachstate
功能:设置线程分离属性。
int pthread_attr_setdetachstate (pthread_attr_t* attr, int detachstate);参数:attr:线程属性。
detachstate:PTHREAD_CREATE_DETACHED(分离)
PTHREAD_CREATE_JOINABLE(非分离)
返回值:成功: 0 失败: -1
4、pthread_attr_getschedparam
功能:得到线程优先级。
int pthread_attr_getschedparam (pthread_attr_t* attr, struct sched_param* param);
参数:attr:线程属性;
param:线程优先级;
返回值:成功: 0 失败: -1
5、pthread_attr_setschedparam
功能:设置线程优先级。
int pthread_attr_setschedparam (pthread_attr_t* attr, struct sched_param* param);
参数:attr:线程属性。
param:线程优先级。
返回值:成功: 0 失败: -1
线程互斥
一个进程中的多个线程是共享同一段资源的,在某一时刻,只能有一个线程可以操作共享资源,这种由于对资源的竞争引出了互斥锁.
互斥锁的操作主要包括互斥锁初始化、上锁、判断上锁、解锁、摧毁互斥锁。其中互斥锁可以分为快速互斥锁(fast)、递归互斥锁(recursive),这检错互斥锁(error checking)。这三种锁的区别主要在于调用者在希望得到互斥锁时如果没有得到互斥锁是否需要等待挂起。快速锁是指调用线程会阻塞直到线程锁得到解锁为止。递归锁能够成功地返回并且记录调用线程在互斥上的加锁次数,检错锁则为快速互斥锁的非阻塞版本,它会立即返回并返回一个错误的信息EDEADLK。
1、互斥锁是通过互斥量的数据类型pthread_mutex_t来实现的,主要操作有
(1) 初始化锁函数
int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr);
参数:mutex:互斥锁。
mutexattr:PTHREAD_MUTEX_INITIALIZER:创建快速互斥锁。
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP:创建递归互斥锁。
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP:创建检错互斥锁。
返回值:成功:0 出错:-1
(2) 上锁函数:
int pthread_mutex_lock(pthread_mutex_t* mutex);
pthread_mutex_lock用于对互斥量上锁。如果该互斥量还未上锁,则对其上锁,立即返回。如果该互斥量已经为别的线程锁住,则pthread_mutex_lock阻塞调用线程,直到该互斥量释放。如果该互斥量已经被调用线程锁住,则调用线程再次用pthread_mutex_lock对该互斥量上锁时,处理过程与互斥量的类型有关。
fast - 调用线程阻塞,直到该锁释放。这样倒致调用线程死锁。
error checking - pthread_mutex_lock立即返回,错误码EDEADLK。
recursive - pthread_mutex_lock 函数成功执行,且记录下调用线程对该互斥量上锁的次数。这样的话,解锁时pthread_mutex_unlock调用的次数也必须一致,这样才能将锁置为unlocked状态。
int pthread_mutex_trylock (pthread_mutex_t* mutex);
pthread_mutex_trylock 与pthread_mutex_lock很相似,但是当互斥量已上锁时,此函数
并不阻塞调用线程,此时它只会立即返回并附带错误码EBUSY。
(3) 解锁函数
int pthread_mutex_unlock (pthread_mutex_t* mutex);
释放指定的互斥量。如果互斥量的属性是:
fast - 将其置于unlocked状态
recursive - 对一个互斥量有一个上锁的计数器,此时使该计数器减1, 当计数器减到0时
则释放该互斥量,变为unlocked状态。
(4)销毁锁函数
int pthread_mutex_destroy (pthread_mutex_t* mutex);
参数: mutex:互斥锁。
返回值:成功:0 出错:-1
参考实例
#include
#include
#include
struct prv_context {
int count;
int condition1;
int condition2;
pthread_mutex_t m_lock;
};
struct prv_context *prv_init(void){
struct prv_context *prv;
if ((prv = malloc(sizeof(struct prv_context))) != NULL) {
prv->count = 10;
prv->condition1=1;
prv->condition2=1;
if (pthread_mutex_init(&prv->m_lock, NULL) != 0) {
free(prv);
return NULL;
}
}
printf("prv_init success\n");
return prv;
}
void prv_destroy(struct prv_context *prv){
if(prv!=NULL){
printf("prv_destroy\n");
pthread_mutex_destroy(&prv->m_lock);
free(prv);
}
}
void *prv_thread1(void *context){
struct prv_context *prv=(struct prv_context*)context;
printf("thread1 start run\n");
while(prv->condition1){
pthread_mutex_lock(&prv->m_lock);
prv->count++;
printf("prv_inc count = %d\n", prv->count);
pthread_mutex_unlock(&prv->m_lock);
sleep(1);
}
pthread_exit("thread1 exit");
printf("thread1 stop run\n");
}
void prv_thread1_stop(void *context){
struct prv_context *prv=(struct prv_context*)context;
prv->condition1=0;
printf("thread1 send stop cmd\n");
}
void *prv_thread2(void *context){
struct prv_context *prv=(struct prv_context*)context;
printf("thread2 start run\n");
while(prv->condition2){
pthread_mutex_lock(&prv->m_lock);
if (prv->count > 0) {
prv->count--;
}
printf("prv_dec count = %d\n", prv->count);
pthread_mutex_unlock(&prv->m_lock);
sleep(1);
}
pthread_exit("thread2 exit");
printf("thread2 stop run\n");
}
void prv_thread2_stop(void *context){
struct prv_context *prv=(struct prv_context*)context;
prv->condition2=0;
printf("thread1 send stop cmd\n");
}
int main(void){
int err;
pthread_t tid1, tid2;
struct prv_context *prv;
void * result;
prv = prv_init();
err = pthread_create(&tid1, NULL, prv_thread1, (struct prv_context *)prv);
if (err != 0){
printf("creat thread1 error\n");
}
err = pthread_create(&tid2, NULL, prv_thread2, (struct prv_context *)prv);
if (err != 0){
printf("creat thread2 error\n");
}
sleep(10);
prv_thread1_stop(prv);
prv_thread2_stop(prv);
err = pthread_join(tid1, &result);
if (err != 0){
printf("join thread1 error\n");
}
printf("join thread1 success, result=%s\n",(char*)result);
err = pthread_join(tid2, &result);
if (err != 0){
printf("join thread2 error\n");
}
printf("join thread2 success, result=%s\n",(char*)result);
prv_destroy(prv);
exit(0);
}