1.4 线程间的互斥
互斥:与同步机制不同的点在于无需顺序执行,必须保证同一时间内只允许有一个线程去访问临界资源。实质上是保证同一临界区只允许同一时间运行一个临界资源。
临界资源:多线程共享的易改变的资源
临界区:修改临界资源的代码
注意:同步一定会互斥,但是互斥不一定同步
互斥机制:互斥锁
1.pthread_mutex_init
头文件:#include 原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); 功能:动态初始化一把锁 参数:mutex:锁变量的地址 attr:互斥所得属性,NULL缺省默认 返回值: 总是返回 0
2. 静态初始化一把锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
3. pthread_mutex_lock
头文件:#include 原型:int pthread_mutex_lock(pthread_mutex_t *mutex); 功能:尝试枷锁,如果没有抢到锁资源,阻塞等待加锁 参数:mutex:锁变量的地址 返回值: 成功返回 0 失败返回非0
4.pthread_mutex_trylock
头文件:#include 原型:int pthread_mutex_trylock(pthread_mutex_t *mutex); 功能:尝试加锁,如果没有抢到锁资源,报错返回 参数:mutex:锁变量的地址 返回值: 成功返回 0 失败返回非0
5.pthread_mutex_unlock
头文件:#include 原型:int pthread_mutex_unlock(pthread_mutex_t *mutex); 功能:解锁操作 参数:mutex:锁变量的地址 返回值: 成功返回 0 失败返回非0
6. pthread_mutex_destroy
头文件:#include 原型:int pthread_mutex_destroy(pthread_mutex_t *mutex); 功能:销毁一把锁 参数:mutex:锁变量的地址 返回值: 成功返回 0 失败返回非0
练习
目标将全局变量a加到100 0000,使用两个线程去做,每个线程应该各加50 0000次,这种情况还需要同步吗?
#include <pthread.h> #include <unistd.h> #include <stdio.h> int count = 0; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void * myfun(void * arg) { int i = 500000; while(i) { if(-1 == pthread_mutex_lock(&mutex)) { perror("lock"); return NULL; } //临界区 ---》开始 int a = count; a++; count = a; //临界区 ---》结束 if(-1 == pthread_mutex_unlock(&mutex)) { perror("lock"); return NULL; } i--; } } int main(int argc, char const *argv[]) { pthread_t tid = 0; if(0 != pthread_create(&tid,NULL,myfun,NULL)) { perror("create"); return -1; } int i = 500000; while(i) { if(-1 == pthread_mutex_lock(&mutex)) { perror("lock"); return -1; } int a = count; a++; count = a; if(-1 == pthread_mutex_unlock(&mutex)) { perror("lock"); return -1; } i--; } pthread_join(tid,NULL); pthread_mutex_destroy(&mutex); printf("count = %d\n",count); return 0; }
练习
卖票,共一百张票,有5个人一起卖,设计5个线程,使其票不能重复卖出,并且打印出是哪个线程卖得第多少张票?
#include <stdio.h> #include <stdlib.h> #include <pthread.h> int Ticket = 100; pthread_mutex_t mutex; void delay() { int x = 10000,y; while(x--) { y = 5000; while(y--); } } void * SaleTicket(void *arg) { int cur_ticket; while (1) { pthread_mutex_lock(&mutex); cur_ticket = Ticket; if(cur_ticket <=0) { pthread_mutex_unlock(&mutex); break; } printf("%ld get %d-th ticket!\n",pthread_self(),cur_ticket); cur_ticket--; Ticket = cur_ticket; pthread_mutex_unlock(&mutex); delay(); } } int main(int argc, char const *argv[]) { int i,ret; pthread_t tid[5] = {0}; pthread_mutex_init(&mutex,NULL); //动态初始化一把锁 for(i = 0; i < 5;i++) { ret = pthread_create(&tid[i],NULL,SaleTicket,NULL); if(ret != 0) { perror("create"); return -1; } } for(i = 0; i < 5;i++) { void *status; pthread_join(tid[i],&status); } pthread_mutex_destroy(&mutex); return 0; }
1.5 条件变量
条件变量是为了完成线程间同步指定出来得一种机制,是利用将一个线程挂起等待。然后由另一方发送一个条件成立得信号将其唤醒继续运行得原理。
缺点:等待一方必须要先获取到锁。
1.5.1 函数的接口
1.静态初始化条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
2.动态初始化条件变量
头文件:#include 原型:int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); 功能:动态初始化条件变量 参数:cond:条件变量的地址 attr:使用缺省模式NULL 返回值: 成功返回 0 失败返回非0
3.pthread_cond_wait
头文件:#include 原型:int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 功能:主动挂起等待信号的到来 参数:cond:条件变量的地址 mutex:锁变量的地址 注意:wait函数和一把锁一起绑定,当这个函数运行后, 1.先进性解锁操作,让这把锁的使用权 2.然后将线程挂起 3.当信号到来时,准备运行之前,需要先尝试加锁 1,2属于原子操作 返回值: 成功返回 0 失败返回非0
4.pthread_cond_signal
头文件:#include 原型:int pthread_cond_signal(pthread_cond_t *cond); 功能:发送一个条件成立的信号,一次性的,每次只能唤醒一个线程,谁收到谁执行 参数:cond:条件变量的地址 返回值: 成功返回 0 失败返回非0
5. pthread_cond_broadcast
头文件:#include 原型:int pthread_cond_broadcast(pthread_cond_t *cond); 功能:发送一个条件成立的信号,唤醒所有等待的线程,一个广播信号 参数:cond:条件变量的地址 返回值: 成功返回 0 失败返回非0
6. pthread_cond_destory
头文件:#include 原型:int pthread_cond_destory(pthread_cond_t *cond); 功能: 销毁一个条件变量 参数:cond:条件变量的地址 返回值: 成功返回 0 失败返回非0
#include <stdio.h> #include <stdlib.h> #include <pthread.h> int Ticket = 100; pthread_mutex_t mutex; pthread_cond_t cond; void delay() { int x = 10000,y; while(x--) { y = 5000; while(y--); } } void * SaleTicketA(void *arg) { int cur_ticket; while (1) { pthread_mutex_lock(&mutex); cur_ticket = Ticket; if(cur_ticket <=0) { pthread_mutex_unlock(&mutex); break; } if(cur_ticket == 50) { pthread_cond_signal(&cond); //唤醒另一个线程 } printf("A get %d-th ticket!\n",cur_ticket); cur_ticket--; Ticket = cur_ticket; pthread_mutex_unlock(&mutex); delay(); } } void * SaleTicketB(void *arg) { int cur_ticket; while (1) { pthread_mutex_lock(&mutex); cur_ticket = Ticket; if(cur_ticket <=0) { pthread_mutex_unlock(&mutex); break; } if(cur_ticket >= 50) { pthread_cond_wait(&cond,&mutex); //释放互斥锁,进入睡眠状态 cur_ticket = Ticket; } printf("B get %d-th ticket!\n",cur_ticket); cur_ticket--; Ticket = cur_ticket; pthread_mutex_unlock(&mutex); delay(); } } int main(int argc, char const *argv[]) { int i,ret; pthread_t tid[2] = {0}; pthread_mutex_init(&mutex,NULL); //动态初始化一把锁 pthread_cond_init(&cond,NULL); //初始化条件变量 ret = pthread_create(&tid[0],NULL,SaleTicketA,NULL); if(ret != 0) { perror("create"); return -1; } ret = pthread_create(&tid[1],NULL,SaleTicketB,NULL); if(ret != 0) { perror("create"); return -1; } for(i = 0; i < 2;i++) { void *status; pthread_join(tid[i],&status); } pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); return 0; }