互斥量
int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr);
//亦可 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_lock (pthread_mutex_t* mutex);
int pthread_mutex_unlock (pthread_mutex_t* mutex);
int pthread_mutex_destroy (pthread_mutex_t* mutex);
- 互斥量被初始化为非锁定状态;
- 线程1调用pthread_mutex_lock函数,立即返回,互斥量呈锁定状态;
- 线程2调用pthread_mutex_lock函数,阻塞等待;
- 线程1调用pthread_mutex_unlock函数,互斥量呈非锁定状态;
- 线程2被唤醒,从pthread_mutex_lock函数中返回,互斥量呈锁定状态;
条件变量
条件变量可以让调用线程在满足特定条件的情况下暂停。
int pthread_cond_init (pthread_cond_t* cond,const pthread_condattr_t* attr);
亦可pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
mutexint pthread_cond_wait (pthread_cond_t* cond,pthread_mutex_t* mutex);使调用线程睡入条件变量cond,同时释放互斥锁
int pthread_cond_timedwait (pthread_cond_t* cond, pthread_mutex_t* mutex, const struct timespec* abstime);
int pthread_cond_signal (pthread_cond_t* cond); 从条件变量cond中唤出一个线程,令其重新获得原先的互斥锁
注意:被唤出的线程此刻将从pthread_cond_wait函数中返回,但如果该线程无法获得原先的锁,则会继续阻塞在加锁上。
int pthread_cond_broadcast (pthread_cond_t* cond);从条件变量cond中唤出所有线程
int pthread_cond_destroy (pthread_cond_t* cond);
生产者消费者
生产者:产生数据的线程。
消费者:使用数据的线程。
通过缓冲区隔离生产者和消费者,与二者直连相比,避免相互等待,提高运行效率。
生产快于消费,缓冲区满,撑死。
消费快于生产,缓冲区空,饿死。
1 #include<stdio.h>
2 #include<pthread.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 #include<time.h>
6 #include<string.h>
7 #include<error.h>
8 //定义一个仓库结构体
9 typedef struct StoreHouse{
10 int cnt;//仓库里的数量
11 char store[10];//容量
12 }StoreHouse;
13 //仓库 共享资源
14 StoreHouse s = {};
15
16 pthread_mutex_t mutex;//互斥量
17 pthread_cond_t notfull;//条件变量
18 pthread_cond_t notempty;
19
20 void *producer(void *arg){
21 while(1){//一直生产
22 pthread_mutex_lock(&mutex);//上锁
23 while(s.cnt >= 10){//注意此处用while
24 //使调用线程睡入条件变量cond,同时释放互斥锁mutex
25 pthread_cond_wait(¬full,&mutex);
26 }
27 char p = 'A'+rand()%26;//生产一些字母
28 s.store[s.cnt++] = p;
29 int i;
30 for(i=0;i<s.cnt;i++){//打印一下
31 printf("%c ",s.store[i]);
32 }
33 printf("<----%c\n",p);//打印生产的字母
34 //从条件变量cond中唤出一个线程,令其重新获得原先的互斥锁
35 //被唤出的线程此刻将从pthread_cond_wait函数中返回,但如果该线程无法> 获得原先的锁,则会继续阻塞在加锁上。
36 pthread_mutex_unlock(&mutex);//解锁
37 pthread_cond_signal(¬empty);
38 usleep(rand()%100000);//睡一会
39 }
40 return NULL;
41 }
42
43 void *consumer(void *arg){
44 while(1){//一直消费
45 pthread_mutex_lock(&mutex);
46 while(s.cnt <= 0){
47 pthread_cond_wait(¬empty,&mutex);
48 }
49 char c = s.store[--s.cnt];
50 int i;
51 for(i=0;i<s.cnt;i++){
52 printf("%c ",s.store[i]);
53 }
54 printf("---->%c\n",c);
55 pthread_mutex_unlock(&mutex);
56 pthread_cond_signal(¬full);
57 usleep(rand()%100000);
58 }
59 return NULL;
60 }
61 int main(){
62 srand(time(NULL));//设置随机种子
63 pthread_mutex_init(&mutex,NULL);//初始化
64 pthread_cond_init(¬full,NULL);
65 pthread_cond_init(¬empty,NULL);
66 //创建6个线程,3个生产,3个消费
67 pthread_t ids[6];//线程ID
68 int i;
69 for(i=0;i<6;i++){
70 if(i%2 == 0){
71 int produce = pthread_create(&ids[i],NULL,producer,NULL);
72 if(produce != 0){//线程创建成功返回0,失败返回-1,注意不能用perr or,线程有自己的错误码
73 printf("produce:%s\n",strerror(produce));
74 return -1;
75 }
76 }else{
77 int consume = pthread_create(&ids[i],NULL,consumer,NULL);
78 if(consume != 0){
79 printf("consumer:%s\n",strerror(consume));
80 return -1;
81 }
82 }
83 }
84 for(i=0;i<6;i++){
85 //用来等待线程的结束,线程见同步的操作
86 int join = pthread_join(ids[i],NULL);
87 if(join != 0){
88 printf("join:%s\n",strerror(join));
89 return -1;
90 }
91 }
92 //销毁
93 pthread_mutex_destroy(&mutex);
94 pthread_cond_destroy(¬full);
95 pthread_cond_destroy(¬empty);
96 return 0;
97 }
1.gcc编译时,加上-lphread选项,例:gcc eating.c -lpthread
2.usleep是毫秒级的阻塞
哲学家就餐问题
1965年,著名计算机科学家艾兹格·迪科斯彻,提出并解决了一个他称之为哲学家就餐的同步问题。
从那时起,每个发明同步原语的人,都希望通过解决哲学家就餐问题来展示其同步原语的精妙之处。
这个问题可以简单地描述如下:
五个哲学家围坐在一张圆桌周围,每个哲学家面前都有一盘通心粉。由于通心粉很滑,所以需要两把叉子才能夹住。
相邻两个盘子之间放有一把叉子。哲学家的生活中有两种交替活动时段:即吃饭和思考。
当一个哲学家觉得饿了时,他就试图分两次去取其左边和右边的叉子,每次拿一把,但不分次序。
如果成功地得到了两把叉子,就开始吃饭,吃完后放下叉子继续思考。
1 #include<stdio.h>
2 #include<pthread.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 #include<time.h>
6 #include<string.h>
7 #include<error.h>
8 //初始化互斥量
9 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
10 //初始化条件变量
11 pthread_cond_t cond[5] = {
12 PTHREAD_COND_INITIALIZER,
13 PTHREAD_COND_INITIALIZER,
14 PTHREAD_COND_INITIALIZER,
15 PTHREAD_COND_INITIALIZER,
16 PTHREAD_COND_INITIALIZER,
17 };
18 char *name[5] = {"黄忠","关羽","刘备","张飞","马超"};
19 //定义3种状态,分别为吃饭,挨饿,思考
20 typedef enum State{
21 EATING,
22 HUNGRING,
23 THINKING
24 }State;
25 //一开始的状态都为思考
26 State state[5] = {
27 THINKING,
28 THINKING,
29 THINKING,
30 THINKING,
31 THINKING,
32 };
33 void *dinner(void *arg){//餐桌上
34 int i = (int)arg;
35 while(1){//一直循环吃和思考
36 eat(i);
37 think(i);
38 }
39 }
40 int eat(int i){
41 int l = (i-1+5)%5;//当前哲学家左边的人
42 int r = (i+1)%5;//当前哲学家右边的人
43 pthread_mutex_lock(&mutex);//上锁
44 while(state[l] == EATING || state[r] == EATING){//左边右边有一个在吃就等着
45 printf("%s正在挨饿\n",name[i]);
46 state[i] = HUNGRING;//状态变为挨饿
47 pthread_cond_wait(&cond[i],&mutex);
48 }
49 printf("%s开始吃饭了\n",name[i]);
50 state[i] = EATING;
51 pthread_mutex_unlock(&mutex);//解锁
52 usleep(rand()%100000);
53 }
54 int think(int i){
55 int l = (i-1+5)%5;
56 int r = (i+1)%5;
57 pthread_mutex_lock(&mutex);//上锁
58 printf("%s吃饱了开始思考如何打仗了\n",name[i]);
59 state[i] = THINKING;//修改状态为思考
60 pthread_mutex_unlock(&mutex);//解锁
61 pthread_cond_signal(&cond[l]);//给左边发信号去唤醒
62 pthread_cond_signal(&cond[r]);//给右边发信号去唤醒
63 usleep(rand()%100000);
64 }
65 int main(){
66 srand(time(NULL));
67 pthread_t ids[5];
68 int i;
69 for(i=0;i<5;i++){
70 int ret = pthread_create(&ids[i],NULL,dinner,(void *)i);
71 if(ret != 0){
72 printf("pthread_create:%s\n",strerror(ret));
73 return -1;
74 }
75 }
76 for(i=0;i<5;i++){
77 int join = pthread_join(ids[i],NULL);
78 if(join != 0){
79 printf("join:%s\n",strerror(join));
80 return -1;
81 }
82 }
83 return 0;
84 }
pthread_create的第4个参数,一般为(void*)(&参数) 这样,函数中转换类型为* (参数类型 *)arg