线程同步与互斥。(互斥量与条件变量、Posix信号量)

线程互斥:
所谓互斥:即是间接制约关系,指系统中的某些共享资源,一次只允许一个线程访问,当一个线程正在访问该临界资源时,其它线程必须等待。

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)

好了,具体的代码会在下篇博客生产者、消费者模型中呈现。

猜你喜欢

转载自blog.csdn.net/xiaodu655/article/details/80485150
今日推荐