互斥锁、条件变量、读写锁、自旋锁、信号量

互斥锁

条件变量

读写锁

自旋锁

信号量

互斥锁

特点
  • 原子性:把一个互斥量锁定为一个原子操作,这意味着操作系统(或pthread函数库)保证了如果一个线程锁定了一个互斥量,没有其他线程在同一时间可以成功锁定这个互斥量

  • 唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有其他线程可以锁定这个互斥量;

  • 非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。

操作流程和常用api
【互斥锁的操作流程如下】:

1. 在访问共享资源后临界区域前,对互斥锁进行加锁;

2. 在访问完成后释放互斥锁导上的锁。在访问完成后释放互斥锁导上的锁;

3. 对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。对互斥锁进行加锁后,任何其他试图再次对互斥锁加锁的线程将会被阻塞,直到锁被释放。

#include <pthread.h>
#include <time.h>
// 初始化一个互斥锁。
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
// 对互斥锁上锁,若互斥锁已经上锁,则调用者一直阻塞,
// 直到互斥锁解锁后再上锁。
int pthread_mutex_lock(pthread_mutex_t *mutex);
// 调用该函数时,若互斥锁未加锁,则上锁,返回 0;
// 若互斥锁已加锁,则函数直接返回失败,即 EBUSY。
int pthread_mutex_trylock(pthread_mutex_t *mutex);
// 当线程试图获取一个已加锁的互斥量时,pthread_mutex_timedlock 互斥量
// 原语允许绑定线程阻塞时间。即非阻塞加锁互斥量。
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
const struct timespec *restrict abs_timeout);
// 对指定的互斥锁解锁。
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// 销毁指定的一个互斥锁。互斥锁在使用完毕后,
// 必须要对互斥锁进行销毁,以释放资源。
int pthread_mutex_destroy(pthread_mutex_t *mutex);

条件变量

特点
  • 条件变量是利用线程间共享的全局变量进行同步 的一种机制,主要包括两个动作:

    • 一个线程等待"条件变量的条件成立"而挂起;

    • 另一个线程使 “条件成立”(给出条件成立信号)。

操作流程
1. 初始化:init()或者pthread_cond_tcond=PTHREAD_COND_INITIALIER;属性置为NULL;

2. 等待条件成立:pthread_wait,pthread_timewait.wait()释放锁,并阻塞等待条件变量为真 timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait);

3. 激活条件变量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)

4. 清除条件变量:destroy;无线程等待,否则返回EBUSY清除条件变量:destroy;无线程等待,否则返回EBUSY
#include <pthread.h>
// 初始化条件变量
int pthread_cond_init(pthread_cond_t *cond,
						pthread_condattr_t *cond_attr);
// 阻塞等待
int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
// 超时等待
int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,
						const timespec *abstime);
// 解除所有线程的阻塞
int pthread_cond_destroy(pthread_cond_t *cond);
// 至少唤醒一个等待该条件的线程
int pthread_cond_signal(pthread_cond_t *cond);
// 唤醒等待该条件的所有线程
int pthread_cond_broadcast(pthread_cond_t *cond);

读写锁

特点
  • 读写锁与互斥量类似,不过读写锁允许更改的并行性,也叫共享互斥锁.读写锁可以有3种状态:读模式下加锁状态、写模式加锁状态、不加锁状态。一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁(允许多个线程读但只允许一个线程写)

    • 读写锁的特点

      • 如果有其它线程读数据,则允许其它线程执行读操作,但不允许写操作
      • 如果有其它线程写数据,则其它线程都不允许读、写操作
    • 读写锁的规则

      • 如果某线程申请了读锁,其它线程可以再申请读锁,但不能申请写锁
      • 如果某线程申请了写锁,其它线程不能申请读锁,也不能申请写锁
      • 读写锁适合于对数据结构的读次数比写次数多得多的情况
#include <pthread.h>
// 初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr); 
// 申请读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock ); 
// 申请写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock ); 
// 尝试以非阻塞的方式来在读写锁上获取写锁,
// 如果有任何的读者或写者持有该锁,则立即失败返回。
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); 
// 解锁
int pthread_rwlock_unlock (pthread_rwlock_t *rwlock); 
// 销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

自旋锁

特点
  • 自旋锁与互斥量功能一样,唯一一点不同的就是互斥量阻塞后休眠让出cpu,而自旋锁阻塞后不会让出cpu,会一直忙等待,直到得到锁。

  • 自旋锁在用户态使用的比较少,在内核使用的比较多!自旋锁的使用场景:锁的持有时间比较短,或者说小于2次上下文切换的时间。

  • 自旋锁在用户态的函数接口和互斥量一样,把pthread_mutex_xxx()中mutex换成spin,如:pthread_spin_init()。

信号量(同步和互斥)

特点
  • 信号量广泛用于进程或线程间的同步和互斥,信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问。编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于 0 时,则可以访问,否则将阻塞。
#include <semaphore.h>
// 初始化信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
// 信号量 P 操作(减 1)
int sem_wait(sem_t *sem);
// 以非阻塞的方式来对信号量进行减 1 操作
int sem_trywait(sem_t *sem);
// 信号量 V 操作(加 1)
int sem_post(sem_t *sem);
// 获取信号量的值
int sem_getvalue(sem_t *sem, int *sval);
// 销毁信号量
int sem_destroy(sem_t *sem);
示例
  #include <iostream>
  #include <unistd.h>
  #include <semaphore.h>
  #include <pthread.h>

  sem_t sem;

  void myPrintf(char *str)
  {
      sem_wait(&sem);
      while(*str)                                                                                                        
      {
          putchar(*str);
          fflush(stdout);
          str++;
          sleep(1);
      }
      printf("\n");
      sem_post(&sem);
  }

  void * pthread_cback1(void *arg)
  {
      myPrintf((char*)arg);
 }
  void * pthread_cback2(void *arg)
  {
      myPrintf((char*)arg);
 }
  int main()
  {
      sem_init(&sem,0,1);
      pthread_t tid[2];
      char *name;
     name = "hello";
      pthread_create(&tid[0],NULL,pthread_cback1,name);
     name = "world";
      pthread_create(&tid[1],NULL,pthread_cback2,name);
      pthread_join(tid[0],NULL);
      pthread_join(tid[1],NULL);

      sem_destroy(&sem);
      return 0;
  }

返回顶部

posted on 2021-07-18 17:04  lodger47  阅读(259)  评论(0)    收藏  举报

导航