1.linux多线程概述
1.1概述
1.2线程的分类
1.3线程创建的Linux实现
2.进程的创建和退出
创建线程实际上就是确定调用该线程函数的入口点,线程的创建采用函数pthread_create。在线程创建以后,就开始运行相关的线程函数,在该函数运行完之后,线程就退出,这也是线程退出的一种方式。另一种线程退出的方式是使用函数pthread_exit()函数,这是线程主动退出行为。这里要注意的是,在使用线程函数时,不能随意使用exit退出函数进行出错处理,由于exit的作用是使调用进程终止,往往一个进程包括了多个线程,所以在线程中通常使用pthread_exit函数来代替进程中的退出函数exit。
由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放。正如进程之间可以通过wait()函数系统调用来同步终止并释放资源一样,线程之间也有类似的机制,那就是pthread_join函数。pthread_join函数可以用于将当前线程挂起,等待线程的结束。这个函数是一个线程阻塞函数,调用它的函数将一直等待直到被等待的线程结束为止,当函数返回时,被等待线程的资源被回收。
函数原型:
#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 pthid;
pthread_creat(&pthid,NULL,pthfunc,NULL)或者pthread_creat(&pthid,NULL,pthfunc,(void*)3);
pthread_exit(NULL)或者pthread_exit((void*)3) 3作为返回值被pthread_join函数捕获。
pthread_creat用来创建线程,成功返回0,失败 -1
参数thread(pthid)是传出参数,保存新线程的标识
参数attr是一个结构体指针,里面的元素分别指向新线程的运行属性
参数start_routine是一个函数指针没指向新线程的入口函数(函数名)
参数arg用于传递给第三个参数指向的入口函数,可以为NULL
例子1:
#include<stdio.h>
#include<pthread.h>
void* handler(void* arg){
char* p=(char*) arg;
printf("from main:%s\n",p);
sleep(5);
pthread_exit((void*)3);
}
int main(){
pthread_t thd;
char*p ="hello world";
pthread_create(&thd,NULL,handler,(void*)p);
printf("thd; %u\n",(unsigned)thd);
int iret;
printf("joining\n");
pthread_join(thd,(void*)&iret);
printf("iret:%d\n",iret);
return 0;
}
编译的时候带上线程库的选项
3.线程的等待退出
3.1等待进程退出
3.2线程的取消
4.线程的同步和互斥
4.1线程的互斥
#include<stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t mutex ;//传出参数,指向一个锁
int i = 0 ;
void* handler(void* arg)
{
int index ;
for(index = 0; index < 10000 ; index ++ )
{
pthread_mutex_lock(&mutex);//加锁
i ++ ;
pthread_mutex_unlock(&mutex);//解锁
}
}
int main()
{
pthread_t thd1, thd2 ;
char* p = "hello world" ;
int a = 12345;
int iret ;
char* pret ;
pthread_mutex_init(&mutex, NULL);//创建一个锁,传出这个锁的参数
pthread_create(&thd1, NULL,handler,NULL);//创建一个线程,传出这个线程的参数thd1
pthread_create(&thd2, NULL,handler,NULL);//
printf("joining...\n");
pthread_join(thd1, NULL);结束线程
pthread_join(thd2, NULL);
pthread_mutex_destroy(&mutex);销毁这个锁
printf("i: %d \n", i);
return 0 ;
}
/*************************************************************************
> File Name: ticket1.c
> Author: yang
> Mail:[email protected]
> Created Time: 2014年08月26日 星期二 00:15:58
************************************************************************/
#include<stdio.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>
typedef struct tag{
int s_id;
pthread_mutex_t *s_mutex;
}DATA,*pDATA;
int tacket_cnt=20;
void* handler(void *arg){
pDATA p=(pDATA)arg;
printf("%d on:\n",p->s_id);
while(1){
pthread_mutex_lock(p->s_mutex);
if(tacket_cnt==0){
printf("ticket out!\n");
free(p);
pthread_mutex_unlock(p->s_mutex);//如果缺少这个就是多层加锁,死循环了
return (void*) 0;
}
tacket_cnt--;
sleep(1);//防止都是一个站点售票,不给后面的机会,线程进来的都在这排队呢
printf("%d server sell a tacket,all tackets:%d\n",p->s_id,tacket_cnt);
pthread_mutex_unlock(p->s_mutex);
sleep(1);//防止都是一个站点售票,不给后面的机会,因为一个while循环速度很快
}
}
int main(int argc,char *argv[]){
srand(getpid());
int cnt = atoi(argv[1]);
pthread_t *pthread=(pthread_t*)calloc(cnt,sizeof(pthread_t));//动态建立一个数组,就是线程的那个传出参数
pthread_mutex_t mutex;
pthread_mutex_init(&mutex,NULL);
int index;
for(index=0;index<cnt;index++){
pDATA p=(pDATA)calloc(1,sizeof(DATA));//头是首地址
p->s_id=index;
p->s_mutex=&mutex;
pthread_create(pthread+index,NULL,handler,(void*)p);//建立线程啊,如果想传出多个参数,就得建立结构体,p就是个结构体包括(锁和p_id售票站台号)
}
printf("joining.......\n");
for(index=0;index<cnt;index++)
pthread_join(*(pthread+index),NULL);//回收线程啦
pthread_mutex_destroy(&mutex);//销毁锁
}
4.2线程的同步
1.条件变量
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<unistd.h>
#include<pthread.h>
#define CNT 20
typedef struct tag{
int s_arr[CNT+1];
int s_front;
int s_tail;
}QUEUE,*pQUEUE;//定义了一个队列,任务队列
QUEUE my_que;
pthread_cond_t cond_pro,cond_con;
pthread_mutex_t mutex;
pthread_cond_t cond_pro,cond_con;
int que_empty(pQUEUE pq){
return pq->s_front == pq ->s_tail;
}
int que_full(pQUEUE pq){
return (pq->s_tail+1)%(CNT+1) == pq->s_front;
}
int que_cnt(pQUEUE pq){
return (pq->s_tail - pq->s_front + CNT + 1)%(CNT + 1);
}
void *pro_handler(void* arg){
pthread_detach(pthread_self());
while(1){
pthread_mutex_lock(&mutex);
while(que_full(&my_que)){//当队列满的时候,所有生产这都得等着,特别注意这里的while,pthread_cond_wait是先解锁在抢锁,得保证出去这个循环的时候一定不是空
pthread_cond_wait(&cond_pro,&mutex);
}
my_que.s_arr[my_que.s_tail]=rand()%100;
my_que.s_tail=(my_que.s_tail+1)%(CNT+1);
if(que_cnt(&my_que)==1){//从空变成有一个商品了,告诉所有消费者都过来消费
pthread_cond_broadcast(&cond_con);
}
printf("produce a product,total:%d\n",que_cnt(&my_que));
pthread_mutex_unlock(&mutex);
sleep(rand()%3+1);
}
}
void *con_handler(void* arg){
pthread_detach(pthread_self());
while(1){
pthread_mutex_lock(&mutex);
while(que_empty(&my_que)){//当队列空的时候,所有消费者都得等着
pthread_cond_wait(&cond_con,&mutex);
}
my_que.s_front=(my_que.s_front+1)%(CNT+1);
if(que_cnt(&my_que)==(CNT-1)){//当队列从满变成差一个满的时候,告诉所有生产者,可以生产了
pthread_cond_broadcast(&cond_pro);
}
printf("consump a product ,total:%d\n",que_cnt(&my_que));
pthread_mutex_unlock(&mutex);
sleep(2);
}
}
int main(int argc,char *argv[]){
my_que.s_front = my_que.s_tail=0;
int con_cnt,pro_cnt;
pro_cnt = atoi(argv[1]);
con_cnt = atoi(argv[2]);
srand(getpid());
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond_pro,NULL);//等待条件初始化,cond为传出参数
pthread_cond_init(&cond_con,NULL);
pthread_t* arr=(pthread_t*)calloc(con_cnt+pro_cnt,sizeof(pthread_t));//定义线程描述符数组
int index = 0;
while(con_cnt>0){
pthread_create(arr + index,NULL,con_handler,NULL);//建立线程
index++;
con_cnt--;
}
while(pro_cnt>0){
pthread_create(arr + index,NULL,pro_handler,NULL);
index ++;
pro_cnt--;
}
printf("*****");
while(1);
pthread_mutex_destroy(&mutex);//结束锁
pthread_cond_destroy(&cond_pro);//结束条件
pthread_cond_destroy(&cond_con);
return 0;
}