• IPC通信:互斥锁和条件变量


    互斥锁机制(Mutual exclusion,缩写为Mutex)是一种用于多线程编程中,防止两条线程同时对同一公共资源(比如全局变量)进行读写的机制。该目的通过将代码切片成一个一个的临界区域(critical section)达成。临界区域指的是一块对公共资源进行存取的代码,并非一种机制或是算法

    初始化:  在Linux下, 线程的互斥量数据类型是pthread_mutex_t. 在使用前, 要对它进行初始化:对于静态分配的互斥量, 可以把它设置为PTHREAD_MUTEX_INITIALIZER, 或者调用pthread_mutex_init.对于动态分配的互斥量, 在申请内存(malloc)之后, 通过pthread_mutex_init进行初始化, 并且在释放内存(free)前需要调用pthread_mutex_destroy.

      原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restric attr);

      返回值: 成功则返回0, 出错则返回错误编号.

      说明: 如果使用默认的属性初始化互斥量,只需把attr设为NULL

    互斥操作: 对共享资源的访问, 要对互斥量进行加锁, 如果互斥量已经上了锁, 调用线程会阻塞, 直到互斥量被解锁. 在完成了对共享资源的访问后, 要对互斥量进行解锁   

      加锁和解锁函数原型:

      #include <pthread.h>

      int pthread_mutex_lock(pthread_mutex_t *mutex); 

      int pthread_mutex_trylock(pthread_mutex_t *mutex); 

      int pthread_mutex_unlock(pthread_mutex_t *mutex);

      返回值: 成功则返回0, 出错则返回错误编号.

      说明: 具体说一下trylock函数, 这个函数是非阻塞调用模式, 也就是说, 如果互斥量没被锁住, trylock函数将把互斥量加锁, 并获得对共享资源的访问权限; 如果互斥量被锁住了, trylock函数将不会阻塞等待而直接返回EBUSY, 表示共享资源处于忙状态

    测试实例进一步了解互斥

    #include <stdio.h> 
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <errno.h>

    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    int lock_var;
    time_t end_time;

    void pthread1(void *arg);
    void pthread2(void *arg);

    int main(int argc, char *argv[])
    {
    pthread_t id1,id2;
    pthread_t mon_th_id;
    int ret;

    end_time = time(NULL)+10;
    /*互斥锁初始化*/
    pthread_mutex_init(&mutex,NULL);
    /*创建两个线程*/
    ret=pthread_create(&id1,NULL,(void *)pthread1, NULL);
    if(ret!=0)
    perror("pthread cread1");
    ret=pthread_create(&id2,NULL,(void *)pthread2, NULL);
    if(ret!=0)
    perror("pthread cread2");
    pthread_join(id1,NULL);
    pthread_join(id2,NULL);
    exit(0);
    }

    void pthread1(void *arg)
    {
    int i;
    while(time(NULL) < end_time){
    /*互斥锁上锁*/
    if(pthread_mutex_lock(&mutex)!=0){
    perror("pthread_mutex_lock");
    }
    else
    printf("pthread1:pthread1 lock the variable\n");
    for(i=0;i<2;i++){
    sleep(1);
    lock_var++;
    }
    /*互斥锁接锁*/
    if(pthread_mutex_unlock(&mutex)!=0){
    perror("pthread_mutex_unlock");
    }
    else
    printf("pthread1:pthread1 unlock the variable\n");
    sleep(1);
    }
    }

    void pthread2(void *arg)
    {
    int nolock=0;
    int ret;
    while(time(NULL) < end_time){
    /*测试互斥锁*/
    ret=pthread_mutex_trylock(&mutex);
    if(ret==EBUSY)
    printf("pthread2:the variable is locked by pthread1\n");
    else{
    if(ret!=0){
    perror("pthread_mutex_trylock");
    exit(1);
    }
    else
    printf("pthread2:pthread2 got lock.The variable is %d\n",lock_var);
    /*互斥锁接锁*/
    if(pthread_mutex_unlock(&mutex)!=0){
    perror("pthread_mutex_unlock");
    }
    else
    printf("pthread2:pthread2 unlock the variable\n");
    }
    sleep(3);
    }
    }

    zhaoxj$ ./thread_mutex
    pthread1:pthread1 lock the variable
    pthread2:the variable is locked by pthread1
    pthread1:pthread1 unlock the variable
    pthread2:pthread2 got lock.The variable is 2
    pthread2:pthread2 unlock the variable
    pthread1:pthread1 lock the variable
    pthread1:pthread1 unlock the variable
    pthread2:pthread2 got lock.The variable is 4
    pthread2:pthread2 unlock the variable
    pthread1:pthread1 lock the variable
    pthread1:pthread1 unlock the variable
    pthread2:pthread2 got lock.The variable is 6
    pthread2:pthread2 unlock the variable
    pthread1:pthread1 lock the variable
    pthread1:pthread1 unlock the variable

    (实现一段代码原子操作)说明mutex互斥信号量锁住的不是一个变量,而是阻塞住一段程序。如果对一个mutex变量执行了第一次pthread_mutex_lock(mutex)之后,在unlock(mutex)之前的这段时间内,如果有其他线程也执行到了pthread_mutex_lock(mutex),这个线程就会阻塞住,直到之前的线程unlock之后才能执行,由此,实现同步,也就达到保护临界区资源的目的

                                                                                                                                                                                                         条件变量 条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。

    创建和注销                                                                                          

    1)条件变量和互斥锁一样,都有静态动态两种创建方式,静态方式使用PTHREAD_COND_INITIALIZER常量:pthread_cond_t cond=PTHREAD_COND_INITIALIZER;动态方式调用pthread_cond_init()函数:int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr)

    2)注销一个条件变量需要调用pthread_cond_destroy(),只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY。因为Linux实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程。int pthread_cond_destroy(pthread_cond_t *cond)   

    等待和激发:

    int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex)   

    int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t *mutex,const struct timespec *abstime)

    等待条件有两种方式:无条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。 

    无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的竞争条件(Race Condition)。在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在线程挂起进入等待时mutex将被解锁;在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。可概括为:调用pthread_cond_wait()前线程要显示的加锁,pthread_cond_wait()结束后线程要显示的解锁。pthread_cond_wait()被调用时对本线程的互斥量进行行隐式解锁和加锁过程

    激发条件有两种形式:pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。  两者 如果没有等待的线程,则什么也不做。当pthread_cond_t调用pthread_cond_wait进入等待状态时,pthread_mutex_t互斥信号无效. 示例代码                                                                                                                      

     1 #include <stdio.h> 
    2 #include<pthread.h> //多线程所用头文件
    3 #include <semaphore.h> //信号量使用头文件
    4
    5 pthread_cond_t g_cond ; //申明条锁,并用宏进行初始化
    6 pthread_mutex_t g_mutex ;
    7
    8 void threadFun1(void)
    9 {
    10 int i;
    11 pthread_mutex_lock(&g_mutex); //1
    12 pthread_cond_wait(&g_cond,&g_mutex); //如g_cond无信号,则阻塞
    13
    14 for( i = 0;i < 2; i++ ){
    15 printf("thread threadFun1.\n");
    16 sleep(1);
    17 }
    18
    19 pthread_cond_signal(&g_cond);
    20 pthread_mutex_unlock(&g_mutex);
    21 }
    22
    23 int main(void)
    24 {
    25 pthread_t id1; //线程的标识符
    26 pthread_t id2;
    27
    28 pthread_cond_init(&g_cond,NULL); //也可以程序里面初始化
    29 pthread_mutex_init(&g_mutex,NULL); //互斥变量初始化
    30
    31 int i,ret;
    32 ret = pthread_create(&id1,NULL,(void *)threadFun1, NULL);
    33
    34 if ( ret!=0 ) { //不为0说明线程创建失败
    35 printf ("Create pthread1 error!\n");
    36 exit (1);
    37 }
    38
    39 sleep(5); //等待子线程先开始
    40 pthread_mutex_lock(&g_mutex); //2
    41 pthread_cond_signal(&g_cond); //给个开始信号,注意这里要先等子线程进入等待状态在发信号,否则无效
    42 sleep(5); //此刻子线程仍在等待,即子线程未被调度
    43 pthread_mutex_unlock(&g_mutex);//解锁之后 threadfun1 才开始执行
    44
    45 pthread_join(id1,NULL);
    46 pthread_cond_destroy(&g_cond); //释放
    47 pthread_mutex_destroy(&g_mutex); //释放
    48
    49 return 0;
    50 }

    请看红颜色的1和2 ,明明是1先锁了互斥变量,但代码执行到2还是一样可以锁定.为什么会这样呢?

    问题解释:当程序进入pthread_cond_wait等待后,将会把g_mutex进行解锁,当离开pthread_cond_wait之前,g_mutex会重新加锁。所以在main中的g_mutex会被加锁。

  • 相关阅读:
    管道及有名管道(pipe与FIFO)
    详细介绍Linux重定向的使用
    Emacs文件和目录操作模式Dired
    exec家族函数
    Seven file types of Unix system(4.3)
    va_list、va_start、va_arg、va_end的原理与使用
    const int*和int const*的区别
    asp.net 文件加载错误
    终于有了属于自己的家,哈哈,很高兴~~
    难得的一场雪
  • 原文地址:https://www.cnblogs.com/polestar/p/2436330.html
Copyright © 2020-2023  润新知