Chinaunix首页 | 论坛 | 博客
  • 博客访问: 140124
  • 博文数量: 49
  • 博客积分: 2510
  • 博客等级: 少校
  • 技术积分: 595
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-25 23:08
文章分类
文章存档

2011年(1)

2009年(48)

我的朋友

分类: LINUX

2009-03-17 23:51:13

 

linux多线程编程

Linux下线程概述

进程是系统中程序执行和资源分配的基本单位。每个进程有自己的数据段、代码段和堆栈段。

线程通常叫做轻型的进程。线程是在共享内存空间中并发执行的多道执行路径,他们共享一个进程的资源。

因为线程和进程比起来很小,所以相对来说,线程花费更少的CPU资源。

1 进程与线程的关系

线程按照其调度者可分为用户级线程和内核级线程两种。

1)用户级线程

主要解决的是上下文切换的问题,其调度算法和调度过程全部有用户决定。

2)内核级线程

有内核调度机制实现。

现在大多数操作系统都采用用户级线程和内核级线程并存的方法。用户级线程可与内核级线程实现“一对一”,“一对多”的对应关系。

Linux线程实现

以下线程均为用户级线程。在linux中,一般采用Pthread线程库实现线程的访问与控制,由POSIX提出,具有良好的可移植性。

2.1 线程创建与退出

创建线程使用pthread_create函数。在线程创建以后,就开始运行相关的线程函数。线程退出时使用函数pthread_exit,是线程的主动行为。注意进程退出时使用exit函数,线程中用pthread_exit替代exit

由于一个进程中的多个线程共享数据段,因此通常在线程退出后,退出线程所占用的资源并不会随线程结束而释放。所有需要pthread_join函数来等待线程结束,类似于wait系统调用

创建进程:

#include

pthread_create(pthread_t *thread, pthread_attr_t *attr,

void *(*start_routine)(void *),

void *arg)

thread:线程标识符

attr:线程属性设置

start_routine:线程函数起始地址

arg:传递给start_routine的参数

进程退出:

#include

pthread_exit( void *retval )

retvalpthread_exit调用者线程的返回值,可由其他函数和pthread_join来检测获取。

等待进程退出:

#include

pthread_join( pthread_t *thvoid **thread_return )

th:等待线程的标识符

thread_return:用户定义指针,用来存储被等待线程的返回值



线程实例见:thread.c

gcc -lpthread -o thread thread.c

cat thread.c

#include

#include

void thread1(void)

{

int i=0;

for(i=0;i<6;i++)

{

printf("This is a pthread1.\n");

if(i==2)

pthread_exit(0);

sleep(1);

}

}



void thread2(void)

{

int i;

for(i=0;i<3;i++)

printf("This is a pthread2.\n");

pthread_exit(0);

}



int main(void)

{

pthread_t id1,id2;

int i,ret;

ret=pthread_create(&id1,NULL,(void *) thread1,NULL);

if(ret!=0){

printf ("Create pthread error!\n");

exit (1);

}

ret=pthread_create(&id2,NULL,(void *) thread2,NULL);

if(ret!=0)

{

printf ("Create pthread error!\n");

exit (1);

}

pthread_join(id1,NULL);

pthread_join(id2,NULL);

exit (0);

}

2.2 修改线程属性

thread_creat函数中有设置线程属性参数,这些属性包括绑定属性、分离属性、堆栈地址、堆栈大小、优先级。系统默认属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。

分别介绍绑定属性和分离属性。

1、绑定属性

linux系统中,可实现一个用户级线程与一个内核级线程相对应的“一对一”线程机制。绑定属性是指一个用户级线程固定的分配给一个内核级线程。非绑定属性则是指用户级线程与内核级线程的关系不是始终固定的,而是由系统来控制分配。

2、分离属性

分离属性是用来决定一个线程以什么样的方式来终止自己。在非分离情况下,当一个线程结束时,它所占用的系统资源并没有完全释放,也没有真正终止。只有当pthread_join()函数返回时,该线程才释放自己占用的资源。而在分离情况下,一个线程结束时会立即释放它所占用的资源.

3、属性设置

属性设置是由一定函数来完成的,通常调用pthread_attr_init函数进行初始化。设置绑定属性的函数为pthread_attr_setscope,设置分离属性的函数是pthread_attr_setdetachstate,设置线程优先级的相关函数pthread_attr_getschdparm(获取线程优先级)和pthread_attr_setschedparam (设置线程优先级)。在设置完成属性后,调用pthread_creat函数创建线程

线程属性初始化:

#include

int pthread_attr_init (pthread_attr_t *attr)

attr:线程属性

返回值:成功0,错误-1

设置绑定属性:

#include

pthread_attr_setscope(pthread_attr_t *attr, init scope)

attr:线程属性

scopePTHREAD_SCOPE_SYSTEM(绑定)

PTHREAD_SCOPE_PRCESS(非绑定)

返回值:成功0,错误-1

设置分离属性:

#include

pthread_attr_setsetdetachstate(pthread_attr_t *attr, init detachstate)

attr:线程属性

detachstate PTHREAD_CREAT_DETACHED(分离)

PTHREAD_CREAT_JOINABLE(非分离)

返回值:成功0,错误-1

获取线程优先级:

#include

int pthread_attr_getschedparam (pthread_attr_attr *attr,

struct sched_param *param)

attr:线程属性

param:线程优先级

返回值:成功0,错误-1

设置线程优先级:

#include

int pthread_attr_setschedparam (pthread_attr_attr *attr,

struct sched_param *param)

attr:线程属性

param:线程优先级

返回值:成功0,错误-1


线程实例见:pthread.c

cat attr_thread.c

#include

#include

#include

void thread1(void)

{

int i=0;

for(i=0;i<100000;i++)

{

printf("This is a pthread1.\n");

if(i==2)

pthread_exit(0);

// sleep(1);

}

}


void thread2(void)

{

int i;

while(1)

{

for(i=0;i<3;i++)

printf("This is a pthread2.\n");

// sleep(1);

}

pthread_exit(0);

}


int main(void)

{

pthread_t id1,id2;

int i,ret;

/* */

pthread_attr_t attr;

pthread_attr_init(&attr);

pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);


ret=pthread_create(&id1,&attr,(void *) thread1,NULL);

if(ret!=0)

{

printf ("Create pthread error!\n");

exit (1);

}


ret=pthread_create(&id2,NULL,(void *) thread2,NULL);

if(ret!=0)

{

printf ("Create pthread error!\n");

exit (1);

}


pthread_join(id2,NULL);


return (0);

}

2.3 mutex互斥锁线程控制

mutex是一种简单的加锁的方法来控制对共享资源的访问。在同一时刻只能有一个线程掌握某个互斥上的锁,拥有上锁状态的线程能够对共享资源进行访问。若其他线程希望上锁一个已经被上了互斥锁的资源,则该线程挂起,直到上锁的线程释放互斥锁为止。

互斥锁的操作主要包括以下几个步骤:

互斥锁初始化:pthread_mutex_init

互斥锁上锁:pthread_mutex_lock

互斥锁判断上锁:pthread_mutex_trylock

互斥锁解锁:pthread_mutex_unlock

消除互斥锁:pthread_mutex_destroy

互斥锁可分为以下三种:

快速互斥锁:

递归互斥锁:

检错互斥锁:

这三种锁的主要区别在于其他未占有互斥锁的线程在希望得到互斥锁时是否需要阻塞等待。

快速互斥锁是指调用线程会阻塞直到拥有互斥锁的线程释放为止。

递归互斥锁能够成功返回并且增加调用线程在互斥上加锁的次数。

检错互斥锁则为快速互斥锁的阻塞版本,他会立即返回并得到一个错误。

互斥锁初始化:

#include

int pthread_mutex_init(

pthread_mutex_t *mutex,

const pthread_mutex_attr_t *mutexattr)

Mutex:互斥锁

MutexattrPTHREAD_MUTEX_INITIALIZER:

创建快速互斥锁

PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP:

创建递归互斥锁

PTHREAD_REEORCHECK_MUTEX_INITIALIZER_NP:

创建检错互斥锁

互斥锁操作:

#include

int pthread_mutex_lock(pthread_mutex_t *mutex)

int pthread_mutex_trylock(pthread_mutex_t *mutex)

int pthread_mutex_unlock(pthread_mutex_t *mutex)

int pthread_mutex_destroy(pthread_mutex_t *mutex)

Mutex:互斥锁

返回值:成功0,错误-1


互斥锁实例见:mutex.c

gcc -lpthread -o mutex mutex.c

cat mutex.c

#include

#include

#include

#include

#include


pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int lock_var = 0;

time_t end_time;


void pthread1(void *arg);

void pthread2(void *arg);


int main(int argc, char *argv[])

{

pthread_t id1,id2;

pthread_t mon_th_id;

int ret;


end_time = time(NULL)+10;


pthread_mutex_init(&mutex,NULL);


ret=pthread_create(&id1,NULL,(void *)pthread1, NULL);

if(ret!=0)

perror("pthread cread1");


ret=pthread_create(&id2,NULL,(void *)pthread2, NULL);

if(ret!=0)

perror("pthread cread2");


pthread_join(id1,NULL);

pthread_join(id2,NULL);


exit(0);

}


void pthread1(void *arg)

{

int i;

while(time(NULL) < end_time)

{

if(pthread_mutex_lock(&mutex)!=0)

{

perror("pthread_mutex_lock");

}

else

printf("pthread1:pthread1 lock the variable\n");


for(i=0;i<2;i++)

{

sleep(1);

lock_var++;

}


if(pthread_mutex_unlock(&mutex)!=0)

{

perror("pthread_mutex_unlock");

}

else

printf("pthread1:pthread1 unlock the variable\n");


sleep(1);

}

}


void pthread2(void *arg)

{

int nolock=0;

int ret;


while(time(NULL) < end_time)

{

ret=pthread_mutex_trylock(&mutex);

if(ret==EBUSY)

printf("pthread2:the variable is locked by pthread1\n");

else

{

if(ret!=0)

{

perror("pthread_mutex_trylock");

exit(1);

}

else

printf("pthread2:pthread2 got lock.The variable is %d\n",lock_var);

if(pthread_mutex_unlock(&mutex)!=0)

{

perror("pthread_mutex_unlock");

}

else

printf("pthread2:pthread2 unlock the variable\n");

}

sleep(3);

}

}

2.4 信号量线程控制

信号量也就是操作系统中所用到的PV操作,它广泛用于进程或线程间的互斥与同步。

PV操作是对整数计数器信号量sem的操作。一次P操作使sem减一,一次V操作使sem加一。用于互斥时,几个进程(或线程)往往只设置一个信号量sem

用于同步时,往往设置多个信号量,并安排不同的值了来实现它们之间的顺序执行。

Linux实现了POSIX.1的无名信号量,用于线程的同步与互斥。信号量操作函数:

sem_init:用于创建一个信号量,并初始化它。

sem_waitsem_trywait: 相当于P操作,它们都能使信号量减一,两者区别在于当信号量小于零时,sem_wait会阻塞,而sem_trywait则会立即返回。

sem_post:相当于V操作,它将信号量的值加一同时发出信号唤醒等待的进程。

sem_getvalue:得到信号量的值。

sem_destroy:删除信号量。



信号量实例见:sem_num.c sem_syn.c

gcc -o sem_num -lpthread sem_num.c

cat sem_num.c

#include

#include

#include

#include

#include

//#include

#include



int lock_var;

time_t end_time;

sem_t sem;



void pthread1(void *arg);

void pthread2(void *arg);



int main(int argc, char *argv[])

{

pthread_t id1,id2;

pthread_t mon_th_id;

int ret;



end_time = time(NULL)+30;



ret=sem_init(&sem,0,1);

if(ret!=0)

{

perror("sem_init");

}



ret=pthread_create(&id1,NULL,(void *)pthread1, NULL);

if(ret!=0)

perror("pthread cread1");



ret=pthread_create(&id2,NULL,(void *)pthread2, NULL);

if(ret!=0)

perror("pthread cread2");



pthread_join(id1,NULL);

pthread_join(id2,NULL);



exit(0);

}



void pthread1(void *arg)

{

int i;

while(time(NULL) < end_time)

{

sem_wait(&sem);



for(i=0;i<2;i++)

{

sleep(1);

lock_var++;

printf("lock_var=%d\n",lock_var);

}



printf("pthread1:lock_var=%d\n",lock_var);



sem_post(&sem);

sleep(1);

}

}



void pthread2(void *arg)

{

int nolock=0;

int ret;



while(time(NULL) < end_time)

{

sem_wait(&sem);



printf("pthread2:pthread2 got lock;lock_var=%d\n",lock_var);



sem_post(&sem);

sleep(3);

}

}

cat sem_syn.c

#include

#include

#include

#include

#include

#include

#include

int lock_var;

time_t end_time;

sem_t sem1,sem2;



void pthread1(void *arg);

void pthread2(void *arg);



int main(int argc, char *argv[])

{

pthread_t id1,id2;

pthread_t mon_th_id;

int ret;

end_time = time(NULL)+30;

ret=sem_init(&sem1,0,1);

ret=sem_init(&sem2,0,0);

if(ret!=0)

{

perror("sem_init");

}

ret=pthread_create(&id1,NULL,(void *)pthread1, NULL);

if(ret!=0)

perror("pthread cread1");

ret=pthread_create(&id2,NULL,(void *)pthread2, NULL);

if(ret!=0)

perror("pthread cread2");

pthread_join(id1,NULL);

pthread_join(id2,NULL);

exit(0);

}



void pthread1(void *arg)

{

int i;

while(time(NULL) < end_time){

sem_wait(&sem2);

for(i=0;i<2;i++){

sleep(1);

lock_var++;

printf("lock_var=%d\n",lock_var);

}

printf("pthread1:lock_var=%d\n",lock_var);

sem_post(&sem1);

sleep(1);

}

}



void pthread2(void *arg)

{

int nolock=0;

int ret;

while(time(NULL) < end_time){

sem_wait(&sem1);

printf("pthread2:pthread1 got lock;lock_var=%d\n",lock_var);

sem_post(&sem2);

sleep(3);

}

}

阅读(1107) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~