zoukankan      html  css  js  c++  java
  • 2019年8月15日星期四(系统编程) 互斥锁 读写锁 条件变量

    . 线程互斥方式 - 互斥锁。

    1. 什么是互斥锁?特点如何?

    互斥锁是专门用于处理线程互斥的一个方式,它有两种状态:上锁状态/解锁状态。

    特点:如果处理上锁状态,则不能再上锁,直到解锁为止才能再上锁。如果是处于解锁状态,则不能再解锁了,直到上锁了才能再解锁。

    2. 关于线程互斥锁API函数接口?

    0)定义互斥锁的变量(pthread_mutex_t-> 互斥锁的数据类型)

       pthread_mutex_t mutex;

    1)初始化互斥锁  -> pthread_mutex_init()  -> man 3 pthread_mutex_init

    功能: initialize a mutex  -> 初始化互斥锁

    使用格式:

           #include <pthread.h>

           -> 动态初始化

        int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);

          

           mutex:  互斥锁的地址

           attr:   互斥锁的属性,一般填NULL

           返回值:

                  成功:0

                  失败:错误码

           -> 静态初始化

       pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

    2)上锁  -> pthread_mutex_lock() -> man 3 pthread_mutex_lock

    功能:lock a mutex

    使用格式:

           #include <pthread.h>

           int pthread_mutex_lock(pthread_mutex_t *mutex);

           mutex:必须是已经初始化过的互斥锁的地址

           返回值:

                  成功:0

                  失败:错误码

    3)解锁  -> pthread_mutex_unlock() -> man 3 pthread_mutex_unlock

    功能:unlock a mutex

    使用格式:

             #include <pthread.h>           

            int pthread_mutex_unlock(pthread_mutex_t *mutex);

               mutex:必须是已经初始化过的互斥锁的地址

           返回值:

                  成功:0

                  失败:错误码

    4)销毁互斥锁  -> pthread_mutex_destroy()  -> man 3 pthread_mutex_destroy

    功能:

    使用格式:

           #include <pthread.h>

           int pthread_mutex_destroy(pthread_mutex_t *mutex);

           mutex:必须是已经初始化过的互斥锁的地址

           返回值:

                  成功:0

                  失败:错误码

      练习1:使用互斥锁完成作业2

    #include "head.h"

     

    pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

     

    //Jack发

    void *routine1(void *arg)

    {

           char *p = (char *)arg;

          

           while(1)

           {

                  //在访问共享内存前必须先上锁

                  pthread_mutex_lock(&m);

                 

                  fgets(p,1024,stdin); //访问共享内存

                 

                  //访问完共享内存后必须要解锁

                  pthread_mutex_unlock(&m);

                 

                  if(strncmp(p,"quit",4) == 0)

                  {

                         break;

                  }

                 

                  usleep(100000);

           }

          

           pthread_exit(NULL);

    }

     

    //Rose收

    void *routine2(void *arg)

    {

           usleep(100000);

           char *p = (char *)arg;

          

           while(1)

           {

                  //在访问共享内存前必须先上锁,由于睡觉了,后上锁

                  pthread_mutex_lock(&m);

                 

                  printf("from Jack:%s",p);

                 

                  pthread_mutex_unlock(&m);

                 

                  if(strncmp(p,"quit",4) == 0)

                  {

                         break;

                  }

                 

                  usleep(200000);

           }

          

           pthread_exit(NULL);

    }

     

    int main(int argc,char *argv[])

    {

           //1. 申请key与ID号

           key_t key = ftok(".",10);

           int shmid = shmget(key,1024,IPC_CREAT|0666);

          

           //2. 映射内存空间

           char *p = shmat(shmid,NULL,0);

          

           //3. 产生Jack线程与Rose线程

           pthread_t tid1,tid2;

           pthread_create(&tid1,NULL,routine1,(void *)p);

           pthread_create(&tid2,NULL,routine2,(void *)p);

          

           //4. 接合线程

           pthread_join(tid1,NULL);

           pthread_join(tid2,NULL);

          

           //5. 撤销映射,删除共享内存ID

           shmdt(p);

           shmctl(shmid,IPC_RMID,NULL);

          

           return 0;

    }

     

    结论:只要工程涉及到共享资源(共享内存,链表,文件..),就必须在访问前上锁,访问后解锁。

    . 读写锁。

    1. 互斥锁有什么缺陷?

    互斥锁无论是访问资源,还是修改资源都必须要上锁,而且在上锁期间不能被别的线程再上锁。

    访问资源(一起读这本书) -> 同时上读锁  ->  读锁其实是一把共享锁。

    修改资源(修改书本的数据)  -> 不能同时上写锁  -> 写锁其实是一把互斥锁。

    即有读锁,又有写锁,那么我们就称之为读写锁。

    2. 关于读写锁的API函数接口?

    1)初始化读写锁  -> pthread_rwlock_init()  -> man 3 pthread_rwlock_init

    功能:initialize a read-write lock object

    使用格式:

            #include <pthread.h>

           int pthread_rwlock_init(pthread_rwlock_t *rwlock,const pthread_rwlockattr_t *attr);

           rwlock: 读写锁的地址

           attr: 属性,一般填NULL

           返回值:

                  成功:0

                  失败:错误码

    2)读锁上锁  -> pthread_rwlock_rdlock()  -> man 3 pthread_rwlock_rdlock

    功能:lock a read-write lock object for reading

    使用格式:

           #include <pthread.h>

           int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

           rwlock:读写锁的地址

           返回值:

                  成功:0

                  失败:错误码

    3)写锁上锁  -> pthread_rwlock_wrlock()  -> man 3 pthread_rwlock_wrlock

    功能:lock a read-write lock object for writing

    使用格式:

           #include <pthread.h>

           int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

           rwlock:读写锁的地址

           返回值:

                  成功:0

                  失败:错误码

    4)读写锁解锁  -> pthread_rwlock_unlock()  -> man 3 pthread_rwlock_unlock

    功能:unlock a read-write lock object

    使用格式:

           #include <pthread.h>

           int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

           rwlock:读写锁的地址

           返回值:

                  成功:0

                  失败:错误码

    5)销毁读写锁  -> pthread_rwlock_destroy()  -> man 3 pthread_rwlock_destroy

    功能:destroy a read-write lock object

    使用格式:

           #include <pthread.h>

           int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

           rwlock:读写锁的地址

           返回值:

                  成功:0

                  失败:错误码

      练习2:临界资源"int a",两个线程想打印a的值,两个线程想修改a的值。

             验证:

                  1)读锁可以同时上,但是写锁不可以同时上。

                  2)读锁写锁能不能同时上?  -> 不可以,写锁等到读锁解开之后才能上写锁。

    #include "head.h"

     

    int a = 100;//临界资源

    pthread_rwlock_t rwlock; //读写锁变量

     

    //线程1任务:打印a的值,花了3秒

    void *fun1(void *arg)

    {

           //访问临界资源前,需要上读锁

           pthread_rwlock_rdlock(&rwlock);

           printf("fun1 rdlock lock! ");

           printf("fun1 a = %d ",a);

           sleep(3);

          

           //访问完临界资源,需要解锁

           pthread_rwlock_unlock(&rwlock);

           printf("fun1 rdlock unlock! ");

           pthread_exit(NULL);

    }

     

    //线程2任务:打印a的值,花了5秒

    void *fun2(void *arg)

    {

           //访问临界资源前,需要上读锁

           pthread_rwlock_rdlock(&rwlock);

           printf("fun2 rdlock lock! ");

           printf("fun2 a = %d ",a);

           sleep(5);

          

           //访问完临界资源,需要解锁

           pthread_rwlock_unlock(&rwlock);

           printf("fun2 rdlock unlock! ");

           pthread_exit(NULL);

    }

     

     

    //线程3任务:修改a的值,花了4S  a = 50

    void *fun3(void *arg)

    {

           pthread_rwlock_wrlock(&rwlock);

           printf("fun3 wrlock lock! ");

          

           a = 50;

           sleep(4);

           pthread_rwlock_unlock(&rwlock);

           printf("fun3 wrlock unlock! ");

           pthread_exit(NULL);

    }

     

    //线程4任务:修改a的值,花了6S  a = 30

    void *fun4(void *arg)

    {

           pthread_rwlock_wrlock(&rwlock);

           printf("fun4 wrlock lock! ");

           a = 30;

           sleep(6);

           pthread_rwlock_unlock(&rwlock);

           printf("fun4 wrlock unlock! ");

           pthread_exit(NULL);

    }

     

    void *routine(void *arg)

    {

           int i;

           for(i=0;i<1000000;i++)

           {

                  printf("%d ",i);

                  sleep(1);

           }

    }

     

    int main(int argc,char *argv[])

    {

           //0. 计算时间的流逝

           pthread_t tid;

           pthread_create(&tid,NULL,routine,NULL);

          

           //1. 初始化读写锁

           pthread_rwlock_init(&rwlock,NULL);

          

           //2. 产生子线程

           pthread_t tid1,tid2,tid3,tid4;

           pthread_create(&tid1,NULL,fun1,NULL);

           pthread_create(&tid2,NULL,fun2,NULL);

          

           pthread_create(&tid3,NULL,fun3,NULL);

           pthread_create(&tid4,NULL,fun4,NULL);

          

           //3. 接合线程

           pthread_join(tid1,NULL);

           pthread_join(tid2,NULL);

           pthread_join(tid3,NULL);

           pthread_join(tid4,NULL);

          

           //4. 销毁读写锁

           pthread_rwlock_destroy(&rwlock);

          

           return 0;

    }

    . 条件变量。

    1. 什么是条件变量?特点?

    线程因为某个条件不成立/情况不允许情况下,进入一个变量中等待,这个可以存放这些线程的变量就叫做条件变量。

    条件变量一定是与互斥锁一起使用。

    2. 关于条件变量的函数接口?

    0)定义条件变量 (数据类型:pthread_cond_t)

       pthread_cond_t v;

    1)初始化条件变量?  -> pthread_cond_init() -> man 3 pthread_cond_init

    功能:initialize condition variables

    使用格式:

           动态初始化:

            #include <pthread.h>

           int pthread_cond_init(pthread_cond_t *cond,const pthread_condattr_t *attr);

           cond: 条件变量的地址

           attr: 条件变量的属性,一般填NULL。

           返回值:

                  成功:0

                  失败:错误码

           静态初始化:

           pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

    2)如何进入条件变量中等待?  -> pthread_cond_wait() -> man 3 pthread_cond_wait

       如果满足进入条件变量的条件,则会进去等待,并且会自动解锁。

    功能:wait on a condition

    使用格式:

           #include <pthread.h>

         int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);

           cond:条件变量的地址

           mutex:互斥锁的地址

           返回值:

                  成功:0

                  失败:错误码

    3)如何唤醒条件变量中等待的线程? 

    广播: 唤醒所有在条件变量中等待的线程。  -> pthread_cond_broadcast() -> man 3 pthread_cond_broadcast

    单播: 随机唤醒条件变量中任意一个线程。  -> pthread_cond_signal()    -> man 3 pthread_cond_signal

    功能:broadcast or signal a condition     

    使用格式:

           #include <pthread.h>

           int pthread_cond_broadcast(pthread_cond_t *cond);

           int pthread_cond_signal(pthread_cond_t *cond);

           cond:条件变量的地址     

           返回值:

                  成功:0

                  失败:错误码

    注意: 从条件变量中出来的线程,会自动上锁。

    4)销毁条件变量  -> pthread_cond_destroy()  -> man 3 pthread_cond_destroy

    功能:destroy condition variables

    使用格式:

           #include <pthread.h>

           int pthread_cond_destroy(pthread_cond_t *cond);

           cond:条件变量的地址

           返回值:

                  成功:0

                  失败:错误码

    #include "head.h"

    pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;//初始化互斥锁

    pthread_cond_t v = PTHREAD_COND_INITIALIZER;//初始化条件变量

    int sum = 400;

     

    //任务: 就是去银行卡扣200块

    void *routine(void *arg)

    {

           //1. 访问临界资源(银行卡)前,都需要上锁

           pthread_mutex_lock(&m);

          

           //2. 询问条件是否满足?

           while(sum < 200) //当拿不到钱时

           {

                  //就进入条件变量中等待

                  pthread_cond_wait(&v,&m); //自动解锁。

           }

          

           /* 从循环出来,一定是被唤醒并且余额>=200 */

           printf("before money:%d ",sum);

           //拿钱

           sum -= 200;

           printf("after money:%d ",sum);

          

           //拿到钱后,要主动解锁。

           pthread_mutex_unlock(&m);

          

           //走人

           pthread_exit(NULL);

    }

     

    void *routine1(void *arg)

    {

           int i;

           for(i=0;i<1000000;i++)

           {

                  printf("%d ",i);

                  sleep(1);

           }

    }

     

    int main(int argc,char *argv[])

    {

           //0. 计算时间的流逝

           pthread_t tid_test;

           pthread_create(&tid_test,NULL,routine1,NULL);

          

           int i;

           pthread_t tid[5];

           //1. 产生5个子线程

           for(i=0;i<5;i++)

           {

                  pthread_create(&tid[i],NULL,routine,NULL);

           }

          

           sleep(5);  //2个能拿到钱,3个在条件变量中睡觉。

           /* 主线程在访问临界资源前,也必须上锁 */

           pthread_mutex_lock(&m);

          

           sum += 400;

           printf("main thread + 400! ");

          

           //拿完钱后,需要解锁

           pthread_mutex_unlock(&m);

          

           sleep(2);

          

           //唤醒所有的线程起来拿钱

           pthread_cond_broadcast(&v); //2个拿到钱,还有1个因为拿不到钱再回去睡觉。

           printf("broarcast! ");

          

           sleep(3);

          

           pthread_mutex_lock(&m);

          

           sum += 200;

           printf("main thread + 200! ");

          

           //拿完钱后,需要解锁

           pthread_mutex_unlock(&m);

          

           pthread_cond_signal(&v);

           printf("signal! ");

          

           for(i=0;i<5;i++)

           {

                  pthread_join(tid[i],NULL);

           }

          

           pthread_mutex_destroy(&m);

           pthread_cond_destroy(&v);

          

           return 0;

    }

  • 相关阅读:
    Microsoft NLayerApp“.NET研究”案例理论与实践 项目简介与环境搭建 狼人:
    .NET中的“.NET研究”异步编程:使用F#简化异步编程 狼人:
    ASP.NET MV“.NET研究”C3 基础教程 – Web Pages 1.0 狼人:
    引用“.NET研究”类型赋值为null与加速垃圾回收 狼人:
    使用WCF实现SOA面向服务编程“.NET研究”—— 架构设计 狼人:
    MEF——.NET中值“.NET研究”得体验的精妙设计 狼人:
    Silverlight“.NET研究” 2.5D RPG游戏技巧与特效处理:(十)空间分层战斗系统 狼人:
    再次分享一个多选文件上传方案“.NET研究” 狼人:
    C#中标准Dis“.NET研究”pose模式的实现 狼人:
    .NET中的异步编程 IO完成端口以及FileStream.“.NET研究”BeginRead 狼人:
  • 原文地址:https://www.cnblogs.com/zjlbk/p/11359558.html
Copyright © 2011-2022 走看看