2019年8月15日星期四(系统编程)

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!\n");

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

       sleep(3);

      

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

       pthread_rwlock_unlock(&rwlock);

       printf("fun1 rdlock unlock!\n");

       pthread_exit(NULL);

}

 

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

void *fun2(void *arg)

{

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

       pthread_rwlock_rdlock(&rwlock);

       printf("fun2 rdlock lock!\n");

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

       sleep(5);

      

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

       pthread_rwlock_unlock(&rwlock);

       printf("fun2 rdlock unlock!\n");

       pthread_exit(NULL);

}

 

 

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

void *fun3(void *arg)

{

       pthread_rwlock_wrlock(&rwlock);

       printf("fun3 wrlock lock!\n");

      

       a = 50;

       sleep(4);

       pthread_rwlock_unlock(&rwlock);

       printf("fun3 wrlock unlock!\n");

       pthread_exit(NULL);

}

 

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

void *fun4(void *arg)

{

       pthread_rwlock_wrlock(&rwlock);

       printf("fun4 wrlock lock!\n");

       a = 30;

       sleep(6);

       pthread_rwlock_unlock(&rwlock);

       printf("fun4 wrlock unlock!\n");

       pthread_exit(NULL);

}

 

void *routine(void *arg)

{

       int i;

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

       {

              printf("%d\n",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\n",sum);

       //拿钱

       sum -= 200;

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

      

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

       pthread_mutex_unlock(&m);

      

       //走人

       pthread_exit(NULL);

}

 

void *routine1(void *arg)

{

       int i;

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

       {

              printf("%d\n",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!\n");

      

       //拿完钱后,需要解锁

       pthread_mutex_unlock(&m);

      

       sleep(2);

      

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

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

       printf("broarcast!\n");

      

       sleep(3);

      

       pthread_mutex_lock(&m);

      

       sum += 200;

       printf("main thread + 200!\n");

      

       //拿完钱后,需要解锁

       pthread_mutex_unlock(&m);

      

       pthread_cond_signal(&v);

       printf("signal!\n");

      

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

       {

              pthread_join(tid[i],NULL);

       }

      

       pthread_mutex_destroy(&m);

       pthread_cond_destroy(&v);

      

       return 0;

}

上一篇:ReentranReadWriteLock读写锁


下一篇:读写锁 pthread_rwlock