进程间与线程间锁的方式与常用函数

八、关于锁
 锁主要分为互斥锁pthread_mutex/读写锁pthread_rwlock、信号量sem、文件锁flock、记录锁(文件锁的升级版)fcntl控制
 还有条件变量pthread_cond,虽然它不是锁,但经常跟互斥锁配合使用
 互斥锁、信号量可用于进程间和线程间通信
 读写锁用于线程间通信
 文件锁、记录锁用于进程间通信
 
 
1 互斥锁/互斥量
 可用于线程和进程间通信时加锁,通常用于线程
 用于进程间通信时需要在pthread_mutex_init初始化之前,修改其属性为进程间共享
 pthread_mutex_init函数
  初始化一个互斥锁
  int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
  参1:传出参数
  restrict关键字:只用于限制指针,告诉编译器,所有修改该指针指向内存中内容的操作,只能通过本指针完成。不能通过除本指针以外的其他变量或指针修改
  参2:互斥量属性。是一个传入参数,通常传NULL,选用默认属性(线程间共享)。 参APUE.12.4同步属性
  静态初始化:如果互斥锁 mutex 是静态分配的(定义在全局,或加了static关键字修饰),可以直接使用宏进行初始化。
     e.g.  pthead_mutex_t muetx = PTHREAD_MUTEX_INITIALIZER;
  动态初始化:局部变量应采用动态初始化。e.g.  pthread_mutex_init(&mutex, NULL)
  
 设置属性
  pthread_mutexattr_t mattr 类型:  用于定义mutex锁的属性
  
 pthread_mutexattr_init函数:   初始化mutex属性
  int pthread_mutexattr_init(pthread_mutexattr_t *attr);
  
 pthread_mutexattr_destroy函数:  销毁mutex属性
  int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
  
 pthread_mutexattr_setpshared函数: 设置用于进程或线程
  int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
  参2:pshared取值:
   线程锁:PTHREAD_PROCESS_PRIVATE (mutex的默认属性即为线程锁,进程间私有)
   进程锁:PTHREAD_PROCESS_SHARED
 
 pthread_mutex_destroy函数
  销毁一个互斥锁
  int pthread_mutex_destroy(pthread_mutex_t *mutex);
  
 pthread_mutex_lock函数
  加锁。
  int pthread_mutex_lock(pthread_mutex_t *mutex);
  
 pthread_mutex_unlock函数
  解锁。
  int pthread_mutex_unlock(pthread_mutex_t *mutex);
  
 pthread_mutex_trylock函数
  尝试加锁
  int pthread_mutex_trylock(pthread_mutex_t *mutex);
2 信号量
  由于互斥锁的粒度比较大,如果我们希望在多个线程间对某一对象的部分数据进行共享,使用互斥锁是没有办法实现的,只能将整个数据对象锁住。
  这样虽然达到了多线程操作共享数据时保证数据正确性的目的,却无形中导致线程的并发性下降。
  线程从并行执行,变成了串行执行。与直接使用单进程无异。
  信号量,是相对折中的一种处理方式,既能保证同步,数据不混乱,又能提高线程并发。
  信号量的返回值都是成功返回0, 失败返回-1,同时设置errno
  信号量的初值,决定了占用信号量的线程的个数。
  
 信号量基本操作:
  sem_wait:  1. 信号量大于0,则信号量--  (类比pthread_mutex_lock)
     |   2. 信号量等于0,造成线程阻塞
   对应
     |
   sem_post: 将信号量++,同时唤醒阻塞在信号量上的线程 (类比pthread_mutex_unlock)
  但,由于sem_t的实现对用户隐藏,所以所谓的++、--操作只能通过函数来实现,而不能直接++、--符号。
  信号量的初值,决定了占用信号量的线程的个数。
  
 sem_init函数
  初始化一个信号量
   int sem_init(sem_t *sem, int pshared, unsigned int value);
   参1:sem信号量 
   参2:pshared取0用于线程间;取非0(一般为1)用于进程间 
   参3:value指定信号量初值
  
 sem_destroy函数
  销毁一个信号量
   int sem_destroy(sem_t *sem);
   
 sem_wait函数
  给信号量加锁 --
   int sem_wait(sem_t *sem);
 sem_post函数
  给信号量解锁 ++
    int sem_post(sem_t *sem);
   
 sem_trywait函数
  尝试对信号量加锁 -- (与sem_wait的区别类比lock和trylock)
    int sem_trywait(sem_t *sem); 
  
 sem_timedwait函数
  限时尝试对信号量加锁 --
   int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
   参2:abs_timeout采用的是绝对时间。   
   定时1秒:
    time_t cur = time(NULL); 获取当前时间。
  struct timespec t; 定义timespec 结构体变量t
    t.tv_sec = cur+1; 定时1秒
    t.tv_nsec = t.tv_sec +100;
  sem_timedwait(&sem, &t); 传参
3 读写锁
 特性:读共享,写独占,写优先级高
 返回值,成功返回0,失败返回错误号
 
 pthread_rwlock_init函数
  初始化一把读写锁
  int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
  参2:attr表读写锁属性,通常使用默认属性,传NULL即可。
  
 pthread_rwlock_destroy函数
  销毁一把读写锁
  int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
  
 pthread_rwlock_rdlock函数
  以读方式请求读写锁。(常简称为:请求读锁)
  int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
  
 pthread_rwlock_wrlock函数
  以写方式请求读写锁。(常简称为:请求写锁)
  int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
  
 pthread_rwlock_unlock函数
  解锁
  int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
  
 pthread_rwlock_tryrdlock函数
  非阻塞以读方式请求读写锁(非阻塞请求读锁)
  int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
  
 pthread_rwlock_trywrlock函数
  非阻塞以写方式请求读写锁(非阻塞请求写锁)
  int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
  
4 文件锁flock
 int flock(int fd, int operation);
 成功返回0,失败返回-1,同时设置errno
 参数1为文件描述符
 参数2位锁的方式:
  LOCK_SH,共享锁,多个进程共享同一把锁,常被用作读锁;
  LOCK_EX,排他锁,只允许一个进程使用,常被用作写锁;
  LOCK_UN,解锁
  LOCK_NB, 设置非阻塞,若是设置了该属性,尝试加锁时发现锁被其他进程占用
     则返回错误,并设置errno = EWOULDBLOCK
    
5 记录锁(文件锁的升级版)
  利用fcntl来加锁。 
  操作文件的进程没有获得锁时,可以打开文件,但无法read、write。
  这种方式可以实现对文件的某一部分加锁
 int fcntl(int fd, int cmd, ... /* arg */ );
 参数1为文件描述符
 参2:
  F_SETLK (struct flock *) 设置记录锁,非阻塞
  F_SETLKW (struct flock *)  设置记录锁,阻塞
  F_GETLK (struct flock *) 获取记录锁
 参3:结构体地址
        struct flock {
              ...
              short l_type;     锁类型:F_RDLCK 、F_WRLCK 、F_UNLCK
              short l_whence;   * How to interpret l_start: SEEK_SET, SEEK_CUR, SEEK_END */
              off_t l_start;    起始位置偏移/* Starting offset for lock */
              off_t l_len;      锁的长度:0表示整个文件加锁/* Number of bytes to lock */
              pid_t l_pid;      持有该锁的进程ID:(F_GETLK only)
              ...
         };
 用法:
  struct flock l;
  l.l_type = F_WRLCK;
  l.l_whence = SEEK_SET;
  l.l_start = 0;
  l.l_len = 128;
 
  int ret = fcntl(fd, F_SETLKW, &l);
  
6 条件变量
 条件变量不是锁,但他也会造成线程阻塞,通常与互斥锁搭配使用
 返回值,成功返回0,失败返回错误号
 
 如果没有线程在等待条件,此时唤醒函数pthread_cond_signal不会唤醒任何的线程,也不会记录。
 如果有多个线程在执行pthread_cond_wait,
 而此时有一个线程调用pthread_cond_signal,那么只会唤醒其中一个线程。
 如果想唤醒所有线程,那么调用pthread_cond_broadcast,该函数可以唤醒等待该条件的所有线程。
 pthread_cond_init函数
  初始化一个条件变量
  int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);  
  参2:attr表条件变量属性,通常为默认值,传NULL即可
  也可以使用静态初始化的方法,初始化条件变量:
  pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
  
 pthread_cond_destroy函数
  销毁一个条件变量
  int pthread_cond_destroy(pthread_cond_t *cond);
  
 pthread_cond_wait函数
  阻塞等待一个条件变量
   int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
   
  流程:
  第一,获得互斥锁
  第二,
  阻塞等待条件cond(参1)满足,并解锁获得的互斥锁,这一步为一个原子操作。
  第三,当条件满足(被信号唤醒),pthread_cond_wait函数返回,解除阻塞并重新申请加锁;
  第四,解锁
  其中第一和第四步需要手动使用pthread_mutex实现代码
  
  pthread_cond_timedwait函数
  限时等待一个条件变量
  int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
   前面两个参数与pthread_cond_wait相同
   第三个参数是绝对时间,为相对于unix时间戳的时间
    struct timespec {
     time_t tv_sec;  /* seconds */ 秒
     long   tv_nsec; /* nanosecondes*/ 纳秒
    }                
   正确用法:
     time_t cur = time(NULL); 获取当前时间。
     struct timespec t; 定义timespec 结构体变量t
     t.tv_sec = cur+1; 定时1秒
   对比该结构体的相对时间
    struct timeval {
      time_t      tv_sec;  /* seconds */ 秒
      suseconds_t tv_usec;  /* microseconds */ 微秒
    };
    
  pthread_cond_signal函数
  唤醒至少一个阻塞在条件变量上的线程,通常就是唤醒一个
  int pthread_cond_signal(pthread_cond_t *cond);
  
  pthread_cond_broadcast函数
  唤醒全部阻塞在条件变量上的线程
   int pthread_cond_broadcast(pthread_cond_t *cond);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值