线程基本概念
- 刚创建的进程默认有一个线程,成为主控线程(主线程)
Linux线程实现
线程标识
Linux线程的创建和终止
pthread_create
龟兔赛跑案例
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<math.h>void * th_fn(void *arg)
{int distance = (int)arg;int i ;for(i = 1;i<=distance;i++){printf("%lx run %d\n",pthread_self(),i);//int time = (int)(drand48()*100000);sleep(2); //微妙}return (void*)0;
}int main()
{int err;pthread_t rabbit,turtle; // 定义线程标识符// 创建rabbit线程if((err = pthread_create(&rabbit,NULL,th_fn,(void*)50))!=0){perror("pthread_create error");}// 创建turtle线程if((err = pthread_create(&turtle,NULL,th_fn,(void*)50))!=0){perror("pthread_create error");}printf("control thread id:%lx\n",pthread_self());printf("finishe\n");sleep(10); // 让主控线程先睡眠一下return 0;
}
参数arg
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<math.h>typedef struct
{char name[20];int time;int start;int end;
}RaceArg;void * th_fn(void *arg)
{RaceArg *r = (RaceArg*)arg;for(int i = r->start;i<=r->end;i++){printf("%s %lx run %d\n",r->name,pthread_self(),i);usleep(r->time);}return (void*)0;
}int main()
{int err;pthread_t rabbit,turtle; // 定义线程标识符RaceArg r_a = {"rabbit",(int)(drand48()*1000000),20,50};RaceArg t_a = {"turtle",(int)(drand48()*1000000),10,60};// 创建rabbit线程if((err = pthread_create(&rabbit,NULL,th_fn,(void*)&r_a))!=0){perror("pthread_create error");}// 创建turtle线程if((err = pthread_create(&turtle,NULL,th_fn,(void*)&t_a))!=0){perror("pthread_create error");}pthread_join(rabbit,NULL);pthread_join(turtle,NULL);//sleep(10); // 让主控线程先睡眠一下printf("control thread id:%lx\n",pthread_self());printf("finishe\n");return 0;
}
-
效果与原先哪个相同
-
主控线程调用pthread_join(),自己会阻塞。直到rabbit和turtle线程结束方可运行
-
局部变量是各个线程各有一个
-
全局变量,静态变量是共享的。
终止方式
如何在主控进程中获得线程返回的结果
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>typedef struct{int d1;int d2;}Arg;void * th_fn(void *arg){Arg *r = (Arg*)arg;return (void*)(r->d1+r->d2);}int main(void){int err = 0;pthread_t th;Arg r = {20,50};if((err=pthread_create(&th,NULL,th_fn,(void*)&r))!=0){perror("pthread_create error");}int* result;pthread_join(th,(void**)&result);printf("result is %d\n",(int)result);printf("result is %d\n",*result);return 0;}
- 返回结构体指针
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>typedef struct{int d1;int d2;}Arg;void * th_fn(void *arg){Arg *r = (Arg*)arg;return (void*)r;}int main(void){int err = 0;pthread_t th;Arg r = {20,50};if((err=pthread_create(&th,NULL,th_fn,(void*)&r))!=0){perror("pthread_create error");}Arg *result;pthread_join(th,(void**)&result);printf("result: %d",((Arg*)result)->d1+((Arg*)result)->d2);return 0;}
- 修改龟兔赛跑案例1
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<math.h>void * th_fn(void *arg)
{int distance = (int)arg;int i ;for(i = 1;i<=distance;i++){printf("%lx run %d\n",pthread_self(),i);//int time = (int)(drand48()*100000);sleep(2); //微妙}//return (void*)0; //pthread_exit((void*)0);return (void*)distance;
}int main()
{int err;pthread_t rabbit,turtle; // 定义线程标识符// 创建rabbit线程if((err = pthread_create(&rabbit,NULL,th_fn,(void*)50))!=0){perror("pthread_create error");}// 创建turtle线程if((err = pthread_create(&turtle,NULL,th_fn,(void*)50))!=0){perror("pthread_create error");}int result;pthread_join(rabbit,(void*)&result);printf("rabbit's distance is %d\n",result);pthread_join(turtle,(void*)&result);printf("turtle's distance is %d\n",result);//pthread_join(rabbit,NULL);//pthread_join(turtle,NULL);//sleep(10); // 让主控线程先睡眠一下printf("control thread id:%lx\n",pthread_self());printf("finishe\n");return 0;
}
- 修改龟兔赛跑案例2
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<math.h>typedef struct
{char name[20];int time;int start;int end;
}RaceArg;void * th_fn(void *arg)
{RaceArg *r = (RaceArg*)arg;for(int i = r->start;i<=r->end;i++){printf("%s %lx run %d\n",r->name,pthread_self(),i);usleep(r->time);}//return (void*)0;return (void*)(r->end-r->start);
}int main()
{int err;pthread_t rabbit,turtle; // 定义线程标识符RaceArg r_a = {"rabbit",(int)(drand48()*10000000),20,50};RaceArg t_a = {"turtle",(int)(drand48()*10000000),10,60};// 创建rabbit线程if((err = pthread_create(&rabbit,NULL,th_fn,(void*)&r_a))!=0){perror("pthread_create error");}// 创建turtle线程if((err = pthread_create(&turtle,NULL,th_fn,(void*)&t_a))!=0){perror("pthread_create error");}int result;pthread_join(rabbit,(void*)&result);printf("rabbit distance is %d\n",result);pthread_join(turtle,(void*)&result);printf("turtle distance is %d\n",result);//pthread_join(rabbit,NULL);//pthread_join(turtle,NULL);//sleep(10); // 让主控线程先睡眠一下printf("control thread id:%lx\n",pthread_self());//printf("finishe\n");return 0;
}
线程的清理和控制函数
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>// 定义线程清理函数
void clean_fun(void *arg)
{char *s = (char*)arg;printf("clean_func: %s\n",s);
}void * th_fun(void *arg)
{int execute = (int)arg;pthread_cleanup_push(clean_fun,"first clean func");pthread_cleanup_push(clean_fun,"second clean func")printf("thread running %lx\n",pthread_self());pthread_cleanup_pop(execute);pthread_cleanup_pop(execute);return (void*)0;
}int main(void)
{int err;pthread_t th1,th2;if((err=pthread_create(&th1,NULL,th_fun,(void*)1))!=0){perror("pthread create error");}pthread_join(th1,NULL);printf("th1(%lx) finished\n",th1);if((err=pthread_create(&th2,NULL,th_fun,(void*)1))!=0){perror("pthread create error");}pthread_join(th2,NULL);printf("th2(%lx) finished\n",th2);return 0;
}
进程与线程的比较
线程的状态转换
线程属性的初始化和销毁
分离属性
- 可以通过分离属性,在不设置pthread_join的情况下回收线程的资源
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>void out_state(pthread_attr_t *attr)
{int state;if(pthread_attr_getdetachstate(attr,&state)!=0){perror("getdetachstate error\n");}else{if(state==PTHREAD_CREATE_JOINABLE){printf("joinable state\n");}else if(state == PTHREAD_CREATE_DETACHED){printf("detached state\n");}else{printf("error statei");}}
}// 线程运行函数
void * th_fn(void *arg)
{int i;int sum = 0;for(int i =0;i<=100;i++){sum+=i;}return (void*)sum;
}int main(void)
{int err;pthread_t default_th,detach_th;//定义线程属性pthread_attr_t attr;// 对线程属性进行初始化pthread_attr_init(&attr);//输出分离属性out_state(&attr);// 去分离属性的默认值,以正常方式启动子线程if((err=pthread_create(&default_th,&attr,th_fn,(void*)0))!=0){perror("pthread create error");}int res;if((err=pthread_join(default_th,(void*)&res))!=0){perror("pthread join error");}else{printf("default retrun is %d\n",(int)res);}printf("__________________________________________________________________\n");// 设置分离属性为分离状态启动pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);out_state(&attr);// 以分离状态启动子线程if((err=pthread_create(&detach_th,&attr,th_fn,(void*)0))!=0){perror("pthread create error");}if((err=pthread_join(detach_th,(void*)&res))!=0){perror("pthread join error");fprintf(stderr,"%s\n",strerror(err));}else{printf("default retrun is %d\n",(int)res);}//销毁线程属性pthread_attr_destroy(&attr);printf("0x%lx finished\n",pthread_self());return 0;
}
- 设置了分离属性就不需要调用pthread_join了
Linux线程的互斥和同步
线程的互斥
- account.h
#ifndef __ACCOUNT_H__
#define __ACCOUNT_H__
typedef struct{int code;double balance;
}Account;extern Account* create_account(int code,double balance);
extern void destory_account(Account *a);
extern double withdraw(Account *a,double amt);
extern double deposit(Account *a,double amt);
extern double get_balance(Account *a);#endif
- accouont.c
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include"account.h"Account* create_account(int code,double balance)
{Account *a = (Account*)malloc(sizeof(Account));assert(a!=NULL);a->code = code;a->balance = balance;return a;
}
void destory_account(Account *a)
{if(a!=NULL){free(a);a = NULL;}
}
double withdraw(Account *a,double amt)
{if(amt<0 || amt>a->balance)return 0.0;double balance = a->balance;sleep(1);balance -= amt;a->balance = balance;return amt;
}double deposit(Account *a,double amt){if(amt<0)return 0.0;double balance = a->balance;sleep(1);balance -= amt;a->balance = balance;return amt;}double get_balance(Account *a){return a->balance;}
- accouont_test.c
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
#include"account.h"typedef struct{char name[20];Account *account;double amt;
}OperArg;// 定义取款操作的线程运行函数
void * withdraw_fn(void *arg)
{OperArg *oa = (OperArg*)arg;double amt = withdraw(oa->account,oa->amt);printf("%8s(0x%lx) withdraw %f from account %d\n",oa->name,pthread_self(),amt,oa->account->code );
}
//定义存款操作的线程运行函数
void* deposit_fn(void *arg)
{OperArg *oa = (OperArg*)arg;double amt = deposit(oa->account,oa->amt);printf("%8s(0x%lx) deposit %f from account %d\n",oa->name,pthread_self(),amt,oa->account->code );return (void*)0;
}
// 定义检查银行账户的线程运行函数
void check_fn(void *arg)
{return (void*)0;
}
int main(void)
{int err;pthread_t boy,girl;Account *a = create_account(100001,10000);OperArg o1,o2;strcpy(o1.name,"boy");o1.account = a;o1.amt = 10000;strcpy(o2.name,"girl");o2.account = a;o2.amt = 10000;// 同时启动两个线程去运行同一个账户if((err=pthread_create(&boy,NULL,withdraw_fn,(void*)&o1))!=0){perror("pthread create error");}if((err=pthread_create(&girl,NULL,withdraw_fn,(void*)&o2))!=0){perror("pthread create error");}pthread_join(boy,NULL);pthread_join(girl,NULL);printf("account balance:%f\n",get_balance(a));destory_account(a);return 0;
}
- 线程不安全
互斥锁
#ifndef __ACCOUNT_H__
#define __ACCOUNT_H__#include<pthread.h>
typedef struct{int code;double balance;// 定义一把互斥锁,用来对多线程操作。银行账户(共享资源)进行加锁的// 建立互斥锁用来锁定一个账户,和账户绑定在一起// 尽量不设置成全局变量,否则可能出现一把锁去锁几百个账户,导致并发性能降低pthread_mutex_t mutex;
}Account;extern Account* create_account(int code,double balance);
extern void destory_account(Account *a);
extern double withdraw(Account *a,double amt);
extern double deposit(Account *a,double amt);
extern double get_balance(Account *a);#endif
- account.c
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include"account.h"Account* create_account(int code,double balance)
{Account *a = (Account*)malloc(sizeof(Account));assert(a!=NULL);a->code = code;a->balance = balance;// 对互斥锁进行初始化pthread_mutex_init(&a->mutex,NULL);return a;
}
void destory_account(Account *a)
{if(a!=NULL){// 对互斥锁进行销毁pthread_mutex_destroy(&a->mutex);free(a);a = NULL;}
}
double withdraw(Account *a,double amt)
{// 对共享资源进行加锁pthread_mutex_lock(&a->mutex);if(amt<0 || amt>a->balance){// 释放互斥锁pthread_mutex_unlock(&a->mutex);return 0.0;}double balance = a->balance;sleep(1);balance -= amt;a->balance = balance;// 释放互斥锁pthread_mutex_unlock(&a->mutex);return amt;
}double deposit(Account *a,double amt){pthread_mutex_lock(&a->mutex);if(amt<0){pthread_mutex_unlock(&a->mutex);return 0.0;}double balance = a->balance;sleep(1);balance -= amt;a->balance = balance;pthread_mutex_unlock(&a->mutex);return amt;}double get_balance(Account *a){assert(a!=NULL);pthread_mutex_lock(&a->mutex);double balance = a->balance;pthread_mutex_unlock(&a->mutex);return balance;}
互斥锁属性创建和销毁
互斥锁进程共享属性操作
互斥锁类型操作
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>int main(int argc,char *argv[])
{pthread_mutex_t mutex;if(argc<2){printf("-usage:%s [error|normal|recursive]\n",argv[0]);exit(1);}// 定义互斥锁属性pthread_mutexattr_t mutexattr;//初始化互斥锁属性pthread_mutexattr_init(&mutexattr);// 设置互斥锁的类型if(!strcmp(argv[1],"error")){printf("error\n");pthread_mutexattr_settype(&mutexattr,PTHREAD_MUTEX_ERRORCHECK);}else if(!strcmp(argv[1],"normal")){printf("normal\n"); pthread_mutexattr_settype(&mutexattr,PTHREAD_MUTEX_NORMAL);}else if(!strcmp(argv[1],"recursive")){printf("recursive\n");pthread_mutexattr_settype(&mutexattr,PTHREAD_MUTEX_RECURSIVE);}pthread_mutex_init(&mutex,&mutexattr);// 第一次上锁if(pthread_mutex_lock(&mutex)!=0){printf("lock failure\n");}else{printf("lock success\n");}// 第二次上锁if(pthread_mutex_lock(&mutex)!=0){printf("lock failure\n");}else{printf("lock success\n");}pthread_mutex_unlock(&mutex);pthread_mutex_unlock(&mutex);pthread_mutexattr_destroy(&mutexattr);pthread_mutex_destroy(&mutex);return 0;
}
读写锁
- 互斥锁的坏处
一个线程操作时,其他线程不能进行操作
读写锁创建和销毁
读写锁加锁和解锁
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>// 定义读写锁
pthread_rwlock_t rwlock;int main(int argc,char * argv[])
{if(argc<3){printf("-usage:%s [r|w] [r|w]\n",argv[0]);exit(0);}// 读写锁初始化pthread_rwlock_init(&rwlock,NULL);// 第一次上锁if(!strcmp("r",argv[1])){//加读锁if(pthread_rwlock_rdlock(&rwlock)!=0){printf("first read lock failure\n");}else{printf("first read lock success\n");}}else if(!strcmp("w",argv[1])){// 加写锁if(pthread_rwlock_wrlock(&rwlock)!=0){printf("first write lock failure\n");}else{printf("first write lock success\n ");}}// 第二次上锁if(!strcmp("r",argv[2])){//加读锁if(pthread_rwlock_rdlock(&rwlock)!=0){printf("second read lock failure\n");}else{printf("second read lock success\n");}}else if(!strcmp("w",argv[2])){// 加写锁if(pthread_rwlock_wrlock(&rwlock)!=0){printf("second write lock failure\n");}else{printf("second write lock success\n ");}}// 释放读写锁pthread_rwlock_unlock(&rwlock);pthread_rwlock_unlock(&rwlock);return 0;
}
- 修改银行案例互斥锁变为读写锁
#ifndef __ACCOUNT_H__
#define __ACCOUNT_H__#include<pthread.h>
typedef struct{int code;double balance;// 定义读写锁pthread_rwlock_t rwlock;
}Account;extern Account* create_account(int code,double balance);
extern void destory_account(Account *a);
extern double withdraw(Account *a,double amt);
extern double deposit(Account *a,double amt);
extern double get_balance(Account *a);#endif
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include"account_rw.h"Account* create_account(int code,double balance)
{Account *a = (Account*)malloc(sizeof(Account));assert(a!=NULL);a->code = code;a->balance = balance;// 对读写锁进行初始化pthread_rwlock_init(&a->rwlock,NULL);return a;
}
void destory_account(Account *a)
{if(a!=NULL){// 对读写锁进行销毁pthread_rwlock_destroy(&a->rwlock);free(a);a = NULL;}
}
double withdraw(Account *a,double amt)
{// 添加写锁pthread_rwlock_wrlock(&a->rwlock);if(amt<0 || amt>a->balance){// 释放读写锁pthread_rwlock_unlock(&a->rwlock);return 0.0;}double balance = a->balance;sleep(1);balance -= amt;a->balance = balance;// 释放读写锁pthread_rwlock_unlock(&a->rwlock);return amt;
}double deposit(Account *a,double amt){pthread_rwlock_wrlock(&a->rwlock);if(amt<0){pthread_rwlock_unlock(&a->rwlock);return 0.0;}double balance = a->balance;sleep(1);balance -= amt;a->balance = balance;pthread_rwlock_unlock(&a->rwlock);return amt;}double get_balance(Account *a){assert(a!=NULL);pthread_rwlock_rdlock(&a->rwlock);double balance = a->balance;pthread_rwlock_unlock(&a->rwlock);return balance;}
线程同步—条件变量
- 先后执行约束问题
条件变量创建和销毁
条件变量等待操作
条件变量通知操作
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>typedef struct{int res;int is_wait; // 用户给出用于判断pthread_cond_t cond; // 条件变量ipthread_mutex_t mutex; // 互斥锁
}Result;
// 启动两个线程,一个用于计算,一个用于看结果
// 计算并将结果放置到Rexult中的线程运行函数
void * set_fn(void *arg)
{int i = 1,sum = 0;Result *r = (Result*)arg;for(;i<=100;i++)sum+=i;r->res = sum;// 对两个线程共享的判断条件进行保护pthread_mutex_lock(&r->mutex);// 判断获得结果的线程是否准备好while(!r->is_wait){ pthread_mutex_unlock(&r->mutex);usleep(100);pthread_mutex_lock(&r->mutex); }pthread_mutex_unlock(&r->mutex);// 结果已经准备好了,通知唤醒等待的哪个获取结果的线程pthread_cond_broadcast(&r->cond);return (void*)0;
}// 获得结果的线程运行函数
void * get_fn(void * arg)
{Result *r = (Result*)arg;// 对两个线程共享的判断条件进行保护// 两个线程对判断条件是互斥的pthread_mutex_lock(&r->mutex);// 获取结果的线程准备好了r->is_wait = 1;// 获取结果的线程等待pthread_cond_wait(&r->cond,&r->mutex); // 这个实际上就是将自己放入等待队列中// 线程被唤醒后pthread_mutex_unlock(&r->mutex);// 去获取计算的结果int res = r->res;printf("0x%lx get sum is %d\n",pthread_self(),res);return (void*)0;
}int main()
{int err;pthread_t cal,get;Result r;r.is_wait = 0;pthread_cond_init(&r.cond,NULL);pthread_mutex_init(&r.mutex,NULL);// 启动获取结果的线程if((err=pthread_create(&get,NULL,set_fn,(void*)&r))!=0){perror("pthread_create erro");}// 启动计算结果的线程if((err=pthread_create(&cal,NULL,get_fn,(void*)&r))!=0){perror("pthread_create erro");}pthread_join(cal,NULL);pthread_join(get,NULL);pthread_cond_destroy(&r.cond);pthread_cond_destroy(&r.mutex);return 0;
}
- 一个线程计算,2个线程读数据
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>typedef struct{int res;int counter; // 用于统计获取结果线程的数量pthread_cond_t cond; // 条件变量ipthread_mutex_t mutex; // 互斥锁
}Result;
// 启动两个线程,一个用于计算,多个用于看结果
// 计算并将结果放置到Rexult中的线程运行函数
void * set_fn(void *arg)
{int i = 1,sum = 0;Result *r = (Result*)arg;for(;i<=100;i++)sum+=i;r->res = sum;// 对两个线程共享的判断条件进行保护pthread_mutex_lock(&r->mutex);// 判断获得结果的线程是否到达指定数量while(r->counter<2){ pthread_mutex_unlock(&r->mutex);usleep(100);pthread_mutex_lock(&r->mutex); }pthread_mutex_unlock(&r->mutex);// 结果已经准备好了,通知唤醒等待的哪个获取结果的线程 ... 注意这里就不能使用signal,因为要通知多个线程pthread_cond_broadcast(&r->cond);return (void*)0;
}// 获得结果的线程运行函数
void * get_fn(void * arg)
{Result *r = (Result*)arg;// 对两个线程共享的判断条件进行保护// 两个线程对判断条件是互斥的pthread_mutex_lock(&r->mutex);// 获取结果的线程准备好了r->counter++;// 获取结果的线程等待pthread_cond_wait(&r->cond,&r->mutex); // 这个实际上就是将自己放入等待队列中// 线程被唤醒后pthread_mutex_unlock(&r->mutex);// 去获取计算的结果int res = r->res;printf("0x%lx get sum is %d\n",pthread_self(),res);return (void*)0;
}int main()
{int err;pthread_t cal,get,get1;Result r;r.counter = 0;pthread_cond_init(&r.cond,NULL);pthread_mutex_init(&r.mutex,NULL);// 启动获取结果的线程if((err=pthread_create(&get,NULL,get_fn,(void*)&r))!=0){perror("pthread_create erro");}if((err=pthread_create(&get1,NULL,get_fn,(void*)&r))!=0){perror("pthread_create erro");}// 启动计算结果的线程if((err=pthread_create(&cal,NULL,set_fn,(void*)&r))!=0){perror("pthread_create erro");}pthread_join(cal,NULL);pthread_join(get,NULL);pthread_join(get1,NULL);pthread_cond_destroy(&r.cond);pthread_cond_destroy(&r.mutex);return 0;
}
读者-写者问题(线程同步案例)
- 1个写者 1个读者(以前是单向的,现在是双向的)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>typedef struct{int value;pthread_cond_t rc; // 条件变量 pthread_mutex_t rm; // 互斥锁int r_wait; // 用于判断的条件变量pthread_cond_t wc;pthread_mutex_t wm;int w_wait;
}Storage;// 写入数据
void set_data(Storage *s,int value)
{s->value = value;
}// 读取数据
int get_data(Storage *s)
{return s->value;
}// 写着线程指向的线程运行函数
void * set_th(void *arg)
{Storage *s = (Storage*)arg;int i = 1;for(;i<=3;i++){set_data(s,i+3);printf("0x%lx (%-5d) write data:%d\n",pthread_self(),i,i+3);// 读者线程是否准备好pthread_mutex_lock(&s->rm);while(!s->r_wait){pthread_mutex_unlock(&s->rm);sleep(1);pthread_mutex_lock(&s->rm);}s->r_wait = 0; //为下一次读写做准备 pthread_mutex_unlock(&s->rm);pthread_cond_broadcast(&s->rc);// 写者线程等待阻塞// 等待读者读取完数据后通知唤醒她// 然后继续写入数据pthread_mutex_lock(&s->wm);s->w_wait =1; //写者线程准备好了pthread_cond_wait(&s->wc,&s->wm);pthread_mutex_unlock(&s->wm);}return (void*)0;
}//读者线程执行的线程运行函数
void* get_th(void *arg)
{Storage *s = (Storage*)arg;int i = 1;for(;i<=3;i++){pthread_mutex_lock(&s->rm);s->r_wait = 1;pthread_cond_wait(&s->rc,&s->rm);pthread_mutex_unlock(&s->rm);// 读者线程被唤醒后从Storage中读取数:据int value = get_data(s);printf("0x%lx(%-5d) read data: %d\n",pthread_self(),i,value);// 判断写者线程是否准备好while(!s->w_wait){pthread_mutex_unlock(&s->wm);sleep(1);pthread_mutex_lock(&s->wm);}// 唤醒写者线程s->w_wait = 0;pthread_mutex_unlock(&s->wm);pthread_cond_broadcast(&s->wc);}return (void*)0;
}int main(void)
{int err;pthread_t rth,wth;Storage s;s.r_wait = 0;s.w_wait = 0;pthread_mutex_init(&s.rm,NULL);pthread_mutex_init(&s.wm,NULL);pthread_cond_init(&s.rc,NULL);pthread_cond_init(&s.wc,NULL);//创建一个读者线程和写者线程if((err=pthread_create(&rth,NULL,get_th,(void*)&s))!=0){perror("pthread create error");}if((err=pthread_create(&wth,NULL,set_th,(void*)&s))!=0){perror("pthread create error");}pthread_join(rth,NULL);pthread_join(wth,NULL);pthread_mutex_destroy(&s.rm);pthread_mutex_destroy(&s.wm);pthread_cond_destroy(&s.rc);pthread_cond_destroy(&s.wc);}
线程信号量
- 案例
启动三个线程,通过线程信号量来控制其执行次序
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>// 定义线程信号量
sem_t sem1;
sem_t sem2;void* a_fn(void *arg)
{sem_wait(&sem1);printf("thread a running\n");return (void*)0;
}void* b_fn(void *arg)
{sem_wait(&sem2);printf("thread b running\n");//对线程信号量sem1+1,让阻塞的线程a继续运行sem_post(&sem1);return (void*)0;
}void* c_fn(void* arg)
{printf("thread c running\n");// 释放线程b,对线程信号量sem2+1,让阻塞的线程b继续执行sem_post(&sem2);return (void*)0;
}
int main()
{printf("测试线程信号\n");pthread_t a,b,c;// 线程信号量初始化,初始值为0sem_init(&sem1,0,0); // 第1个0表示此线程信号量在同一个进程中使用sem_init(&sem2,0,0); // 第2个0表示此线程信号量的初值pthread_create(&a,NULL,a_fn,(void*)0);pthread_create(&b,NULL,b_fn,(void*)0);pthread_create(&c,NULL,c_fn,(void*)0);// 等待所有线程完成pthread_join(a,NULL);pthread_join(b,NULL);pthread_join(c,NULL);// 销毁线程信号量sem_destroy(&sem1);sem_destroy(&sem2);return 0;
}
互斥
PV操作
- 对银行案例使用它线程信号量进行修改
#ifndef __ACCOUNT_H__
#define __ACCOUNT_H__
#include<semaphore.h>
#include<pthread.h>
typedef struct{int code;double balance;// 定义读写锁 pthread_rwlock_t rwlock; // 定义线程信号量sem_t sem;
}Account;extern Account* create_account(int code,double balance);
extern void destory_account(Account *a);
extern double withdraw(Account *a,double amt);
extern double deposit(Account *a,double amt);
extern double get_balance(Account *a);#endif
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include"account_signal.h"Account* create_account(int code,double balance)
{Account *a = (Account*)malloc(sizeof(Account));assert(a!=NULL);a->code = code;a->balance = balance;// 对读写锁进行初始化 //pthread_rwlock_init(&a->rwlock,NULL);// 初始化线程信号量,初始值设置为1sem_init(&a->sem,0,1);return a;
}
void destory_account(Account *a)
{if(a!=NULL){// 对读写锁进行销毁//pthread_rwlock_destroy(&a->rwlock);// 销毁线程信号量sem_destroy(&a->sem);free(a);a = NULL;}
}
double withdraw(Account *a,double amt)
{// 添加写锁//pthread_rwlock_wrlock(&a->rwlock);// P(1)操作sem_wait(&a->sem);if(amt<0 || amt>a->balance){// 释放读写锁//pthread_rwlock_unlock(&a->rwlock);// V(1)操作sem_post(&a->sem);return 0.0;}double balance = a->balance;sleep(1);balance -= amt;a->balance = balance;// 释放读写锁//pthread_rwlock_unlock(&a->rwlock);// V(1)操作sem_post(&a->sem);return amt;
}double deposit(Account *a,double amt){// pthread_rwlock_wrlock(&a->rwlock);// P(1)操作sem_wait(&a->sem);if(amt<0){// pthread_rwlock_unlock(&a->rwlock);// V(1)操作sem_post(&a->sem);return 0.0;}double balance = a->balance;sleep(1);balance -= amt;a->balance = balance;//pthread_rwlock_unlock(&a->rwlock);// V(1)操作sem_post(&a->sem);return amt;}double get_balance(Account *a){assert(a!=NULL);//pthread_rwlock_rdlock(&a->rwlock);// P(1)sem_wait(&a->sem); double balance = a->balance;//pthread_rwlock_unlock(&a->rwlock);sem_post(&a->sem);return balance;}
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
#include"account_signal.h"typedef struct{char name[20];Account *account;double amt;
}OperArg;// 定义取款操作的线程运行函数
void * withdraw_fn(void *arg)
{OperArg *oa = (OperArg*)arg;double amt = withdraw(oa->account,oa->amt);printf("%8s(0x%lx) withdraw %f from account %d\n",oa->name,pthread_self(),amt,oa->account->code );
}
//定义存款操作的线程运行函数
void* deposit_fn(void *arg)
{OperArg *oa = (OperArg*)arg;double amt = deposit(oa->account,oa->amt);printf("%8s(0x%lx) deposit %f from account %d\n",oa->name,pthread_self(),amt,oa->account->code );return (void*)0;
}
// 定义检查银行账户的线程运行函数
void check_fn(void *arg)
{return (void*)0;
}
int main(void)
{int err;pthread_t boy,girl;Account *a = create_account(100001,10000);OperArg o1,o2;strcpy(o1.name,"boy");o1.account = a;o1.amt = 10000;strcpy(o2.name,"girl");o2.account = a;o2.amt = 10000;// 同时启动两个线程去运行同一个账户if((err=pthread_create(&boy,NULL,withdraw_fn,(void*)&o1))!=0){perror("pthread create error");}if((err=pthread_create(&girl,NULL,withdraw_fn,(void*)&o2))!=0){perror("pthread create error");}pthread_join(boy,NULL);pthread_join(girl,NULL);printf("account balance:%f\n",get_balance(a));destory_account(a);return 0;
}
同步
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>
typedef struct{int res;sem_t sem;
}Result;
// 启动两个线程,一个用于计算,一个用于看结果
// 计算并将结果放置到Rexult中的线程运行函数
void * set_fn(void *arg)
{int i = 1,sum = 0;Result *r = (Result*)arg;for(;i<=100;i++)sum+=i;r->res = sum;// V(1)操作sem_post(&r->sem);return (void*)0;
}// 获得结果的线程运行函数
void * get_fn(void * arg)
{Result *r = (Result*)arg;// P(1)操作sem_wait(&r->sem);// 去获取计算的结果int res = r->res;printf("0x%lx get sum is %d\n",pthread_self(),res);return (void*)0;
}int main()
{int err;pthread_t cal,get;Result r;sem_init(&r.sem,0,1);// 启动获取结果的线程if((err=pthread_create(&get,NULL,set_fn,(void*)&r))!=0){perror("pthread_create erro");}// 启动计算结果的线程if((err=pthread_create(&cal,NULL,get_fn,(void*)&r))!=0){perror("pthread_create erro");}pthread_join(cal,NULL);pthread_join(get,NULL);sem_destroy(&r.sem);return 0;
}
死锁
#include<pthread.h>
#include<string.h>
#include<errno.h>typedef struct
{int value;pthread_mutex_t mutex;
}ResourceA;typedef struct
{int value;pthread_mutex_t mutex;
}ResourceB;typedef struct
{ResourceA *ra;ResourceB *rb;
}Storage;void * a_fun(void *arg)
{Storage *s = (Storage*)arg;// 对ResourceA加锁pthread_mutex_lock(&s->ra->mutex);sleep(1);printf("0x%lx is waiting for ResouceB...\n",pthread_self());// 对ResourceB加锁pthread_mutex_lock(&s->rb->mutex);printf("ResourceA value is:%d\n",s->ra->value);printf("ResourceB value is:%d\n",s->rb->value);pthread_mutex_unlock(&s->ra->mutex);pthread_mutex_unlock(&s->rb->mutex);
}void * b_fun(void *arg)
{Storage *s = (Storage*)arg;// 对ResourceB加锁pthread_mutex_lock(&s->rb->mutex);sleep(1);printf("0x%lx is waiting for ResouceA...\n",pthread_self());// 对ResourceA加锁pthread_mutex_lock(&s->ra->mutex);printf("ResourceA value is:%d\n",s->ra->value);printf("ResourceB value is:%d\n",s->rb->value);pthread_mutex_unlock(&s->ra->mutex);pthread_mutex_unlock(&s->rb->mutex);
}int main(void)
{ResourceA ra;ResourceB rb;ra.value = 100;rb.value = 200;pthread_mutex_init(&ra.mutex,NULL);pthread_mutex_init(&rb.mutex,NULL);Storage s = {&ra,&rb};int err;pthread_t thread_a,thread_b;if((err=pthread_create(&thread_a,NULL,a_fun,(void*)&s))!=0){//fprintf(stderr,"pthread_create:%s\n",strerror("create a"));exit(1);}if((err=pthread_create(&thread_b,NULL,b_fun,(void*)&s))!=0){//fprintf(stderr,"pthread_create:%s\n",strerror("create b"));exit(1);}pthread_join(thread_a,NULL);pthread_join(thread_b,NULL);pthread_mutex_destroy(&ra.mutex);pthread_mutex_destroy(&rb.mutex);return 0;
}
#include<pthread.h>
#include<string.h>
#include<errno.h>typedef struct
{int value;pthread_mutex_t mutex;
}ResourceA;typedef struct
{int value;pthread_mutex_t mutex;
}ResourceB;typedef struct
{ResourceA *ra;ResourceB *rb;
}Storage;void * a_fun(void *arg)
{Storage *s = (Storage*)arg;// 对ResourceA加锁pthread_mutex_lock(&s->ra->mutex);sleep(1);printf("0x%lx is waiting for ResouceB...\n",pthread_self());// 对ResourceB加锁pthread_mutex_lock(&s->rb->mutex);printf("ResourceA value is:%d\n",s->ra->value);printf("ResourceB value is:%d\n",s->rb->value);pthread_mutex_unlock(&s->ra->mutex);pthread_mutex_unlock(&s->rb->mutex);
}void * b_fun(void *arg)
{Storage *s = (Storage*)arg;// 对ResourceA加锁pthread_mutex_lock(&s->ra->mutex);sleep(1);printf("0x%lx is waiting for ResouceA...\n",pthread_self());// 对ResourceB加锁pthread_mutex_lock(&s->rb->mutex);printf("ResourceA value is:%d\n",s->ra->value);printf("ResourceB value is:%d\n",s->rb->value);pthread_mutex_unlock(&s->ra->mutex);pthread_mutex_unlock(&s->rb->mutex);
}int main(void)
{ResourceA ra;ResourceB rb;ra.value = 100;rb.value = 200;pthread_mutex_init(&ra.mutex,NULL);pthread_mutex_init(&rb.mutex,NULL);Storage s = {&ra,&rb};int err;pthread_t thread_a,thread_b;if((err=pthread_create(&thread_a,NULL,a_fun,(void*)&s))!=0){//fprintf(stderr,"pthread_create:%s\n",strerror("create a"));exit(1);}if((err=pthread_create(&thread_b,NULL,b_fun,(void*)&s))!=0){//fprintf(stderr,"pthread_create:%s\n",strerror("create b"));exit(1);}pthread_join(thread_a,NULL);pthread_join(thread_b,NULL);pthread_mutex_destroy(&ra.mutex);pthread_mutex_destroy(&rb.mutex);return 0;
}
线程和信号
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
#include<pthread.h>// 信号处理函数
void sig_handler(int signo)
{printf("pthread is in the sig_hanlder: %lx\n",pthread_self());if(signo==SIGALRM){printf("timeouot...\n");}alarm(2);
}// 线程运行函数
void * th_fn(void *arg)
{if(signal(SIGALRM,sig_handler)==SIG_ERR){perror("signal sigalarm error");}// 在子线程中设置定时器alarm(2);for(int i = 0;i<=100;i++){printf("(%lx) i:%d\n",pthread_self(),i);sleep(1);}return (void*)0;
}int main()
{int err;pthread_t th;pthread_attr_t attr;pthread_attr_init(&attr);// 设置可分离状态属性pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);if((err=pthread_create(&th,&attr,th_fn,(void*)0))!=0){perror("pthread create error");}while(1){printf("control thread(%lx) is running...\n",pthread_self());sleep(10);}printf("control thread over\n");return 0;
}
-
pause():进程暂停等待信号发生
-
sleep(): 进入睡眠,1. 到了时间睡醒了自动运行 2. 信号产生,发送给他,会中断她的睡眠(例如本例子中的中控线程睡眠10秒,明显不够,是被信号叫醒了)
-
上面的程序说明,当子线程设置的睡眠信号,时间到了,是发送个主控线程而不是本子线程
信号屏蔽函数
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
#include<pthread.h>// 信号处理函数
void sig_handler(int signo)
{printf("pthread is in the sig_hanlder: %lx\n",pthread_self());if(signo==SIGALRM){printf("timeouot...\n");}alarm(2);
}// 线程运行函数
void * th_fn(void *arg)
{if(signal(SIGALRM,sig_handler)==SIG_ERR){perror("signal sigalarm error");}// 在子线程中设置定时器alarm(2);for(int i = 0;i<=100;i++){printf("(%lx) i:%d\n",pthread_self(),i);sleep(1);}return (void*)0;
}int main()
{int err;pthread_t th;pthread_attr_t attr;pthread_attr_init(&attr);// 设置可分离状态属性pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);if((err=pthread_create(&th,&attr,th_fn,(void*)0))!=0){perror("pthread create error");}sigset_t set;sigemptyset(&set);sigaddset(&set,SIGALRM);// 对主控线程屏蔽SIGALRM信号pthread_sigmask(SIG_SETMASK,&set,NULL);while(1){printf("control thread(%lx) is running...\n",pthread_self());sleep(10);}printf("control thread over\n");return 0;
}
-
终止上面的线程的三中
- 一直运行直到
- 一直运行直到
-
主动终止。使用
pthread_exit((void*)0)
替换(void*)0
-
利用另外一个线程,发送信号让其终止
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
#include<pthread.h>// 信号处理函数
void sig_handler(int signo)
{printf("pthread is in the sig_hanlder: %lx\n",pthread_self());if(signo==SIGALRM){printf("timeouot...\n");}alarm(2);
}// 线程运行函数
void * th_fn(void *arg)
{if(signal(SIGALRM,sig_handler)==SIG_ERR){perror("signal sigalarm error");}// 在子线程中设置定时器alarm(2);for(int i = 0;i<=100;i++){printf("th1: (%lx) i:%d\n",pthread_self(),i);sleep(1);}return (void*)0;
}void *th_fn2(void* arg)
{pthread_t th1 = (pthread_t)arg;for(int i =1;i<=15;i++){if(i==5){// 终止线程1的运行pthread_cancel(th1);printf("终止线程1的运行\n");alarm(1);}printf("th2: (%lx) i: %d\n",pthread_self(),i);sleep(1);}
}
int main()
{int err;pthread_t th,th2;pthread_attr_t attr;pthread_attr_init(&attr);// 设置可分离状态属性pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);if((err=pthread_create(&th,&attr,th_fn,(void*)0))!=0){perror("pthread create error");}if((err=pthread_create(&th2,&attr,th_fn2,(void*)th))!=0){perror("pthread create error");}sigset_t set;sigemptyset(&set);sigaddset(&set,SIGALRM);// 对主控线程屏蔽SIGALRM信号pthread_sigmask(SIG_SETMASK,&set,NULL);while(1){printf("control thread(%lx) is running...\n",pthread_self());sleep(10);}printf("control thread over\n");return 0;
}