线程互斥:
所谓互斥:即是间接制约关系,指系统中的某些共享资源,一次只允许一个线程访问,当一个线程正在访问该临界资源时,其它线程必须等待。
mutex(互斥量):
- 互斥量本身也是一把锁,提供对资源的独占访问。
- 大部分情况下,线程使用的数据都是局部变量,变量的地址空间在线程栈空间内,变量归属于某个单个线程,其它线程无法获得这种变量。
共享变量:很多变量需要在线程间共享,可以通过数据的共享,完成数据间的交互。
下面我们来看一个买票问题(没加互斥量之前)
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<pthread.h>
5
6 int ticket=100;
7
8 void*thread_run(void*arg)
9 {
10 int a=(int)arg;
11 while(1){
12 if(ticket>0){
13 usleep(10000);
14 printf("%d sells ticket:%d\n",a ,ticket);
15 ticket--;
16 }else{
17 break;
18 }
19 }
20 }
21 int main()
22 {
23 pthread_t t1,t2,t3,t4;
24 void*ret1;
25 void*ret2;
26 void*ret3;
27 void*ret4;
28 pthread_create(&t1,NULL,thread_run,(void*)1);
29 pthread_create(&t2,NULL,thread_run,(void*)2);
30 pthread_create(&t3,NULL,thread_run,(void*)3);
31 pthread_create(&t4,NULL,thread_run,(void*)4);
32
33 pthread_join(t1,&ret1);
34 pthread_join(t2,&ret2);
35 pthread_join(t3,&ret3);
36 pthread_join(t4,&ret4);
37 }
结果显示:
因为每个线程取票的过程并不是原子性的。
下面来看看互斥量的接口;
- 初始化互斥量:
- 方法一:静态分配:
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER
- 方法二:动态分配:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
参数解释:mutex:要初始化的互斥量。
attr:NULL
- 销毁互斥量:
1.静态分配的互斥量不需要销毁。
2.不要销毁一个已经加锁的互斥量。
3.已经销毁的互斥量,要确保后面的不会有线程再尝试加锁。
int pthread_mutex_destroy(pthread_mutex_t *mutex);
互斥量的加锁与解锁:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值:成功返回0,失败返回错误号。
则可改进上边的售票系统:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<pthread.h>
5
6 int ticket=100;
7 pthread_mutex_t mutex;
8
9 void*thread_run(void*arg)
10 {
11 int a=(int)arg;
12 while(1){
13 pthread_mutex_lock(&mutex);//加锁
14 if(ticket>0){
15 usleep(10000);
16 printf("%d sells ticket:%d\n",a ,ticket);
17 ticket--;
18 pthread_mutex_unlock(&mutex);//解锁
19 }else{
20
21 pthread_mutex_unlock(&mutex);//解锁
22 break;
23 }
24 }
25 }
26 int main()
27 {
28 pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//初始化互斥量
29 pthread_t t1,t2,t3,t4;
30 void*ret1;
31 void*ret2;
32 void*ret3;
33 void*ret4;
34 pthread_create(&t1,NULL,thread_run,(void*)1);
35 pthread_create(&t2,NULL,thread_run,(void*)2);
36 pthread_create(&t3,NULL,thread_run,(void*)3);
37 pthread_create(&t4,NULL,thread_run,(void*)4);
39 pthread_join(t1,&ret1);
40 pthread_join(t2,&ret2);
41 pthread_join(t3,&ret3);
42 pthread_join(t4,&ret4);
43 }
运行结果:
线程同步:
1. 条件变量
- 当一个线程互斥地访问某个变量时,它可能发现在其他线程改变状态之前,它什么也做不了。
- 例如一个线程访问队列时,发现队列为空,它只能等待。直到其他线程将一个节点添加到队列中。
- 所以条件变量相当于一个中介。如一个抢答器,两人同时按其的过程。
- 多线程条件下通知某些事件已经就绪的方式。
条件变量函数:
初始化:
int pthread_cond_init(pthread_cond_t *cond,const pthread_condattr_t *attr);
参数:
cond:要初始化的条件变量
attr:NULL
销毁:int pthread_cond_destroy(pthread_cond_t *cond)
等待条件满足:
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
参数:
cond:要在此条件变量上等待
mutex:互斥量,因为在等待时需要将锁释放掉。
唤醒条件:
int pthread_cond_signal(pthread_cond_t *cond);
一个简单的案例:
1 #include<stdio.h>
2 #include<pthread.h>
3 #include<unistd.h>
4 #include<string.h>
5 #include<stdlib.h>
6
7 pthread_cond_t cond;
8 pthread_mutex_t mutex;
9
10 void*r1(void*arg)
11 {
12 while(1){
13 pthread_cond_wait(&cond,&mutex);
14 printf("hello\n");
15 }
16 }
17 void *r2(void*arg)
18 {
19 while(1){
20 pthread_cond_signal(&cond);
21 sleep(1);
22 }
23 }
24 int main()
25 {
26 pthread_t t1,t2;
27 pthread_mutex_init(&mutex,NULL);
28 pthread_create(&t1,NULL,r1,NULL);
29 pthread_create(&t2,NULL,r2,NULL);
30 pthread_join(t1,NULL);
31 pthread_join(t2,NULL);
32 pthread_mutex_destroy(&mutex);
33 pthread_cond_destroy(&cond);
34 }
~
运行结果显示:(每隔1秒打印一个hello world)
2. POSIX信号量(用于实现线程同步)
初始化信号量:
#include<semaphore.h>
int sem_init(sem_t *sem,int pshared,unsigned int value);
参数:
pshared:0表示线程间共享,非0表示进程间共享
value:信号量初始值。
申请信号量不成功时,则申请的线程会被挂起。
销毁信号量:
#include<semaphore.h>
int sem_destroy(sem_t *sem)
等待信号量(p操作)
功能:会将信号量的值减1
int sem_wait(sem_t *sem);
发布信号量(v操作)
功能:表示资源使用完毕,可以归还资源了,将信号量值加1.
int sem_post(sem_t *sem)
好了,具体的代码会在下篇博客生产者、消费者模型中呈现。