首页 > 编程语言 > 详细

c++多线程二 -- 锁

时间:2021-01-15 13:19:07      阅读:31      评论:0      收藏:0      [点我收藏+]

1 线程锁类型

线程之间的锁有:互斥锁、条件锁、自旋锁、读写锁、递归锁。一般而言,锁的功能越强大,性能就会越低。

1.1 互斥锁

互斥锁用于控制多个线程对他们之间共享资源互斥访问的一个信号量。也就是说是为了避免多个线程在某一时刻同时操作一个共享资源。例如线程池中的有多个空闲线程和一个任务队列。任何是一个线程都要使用互斥锁互斥访问任务队列,以避免多个线程同时访问任务队列以发生错乱。

在某一时刻,只有一个线程可以获取互斥锁,在释放互斥锁之前其他线程都不能获取该互斥锁。如果其他线程想要获取这个互斥锁,那么这个线程只能以阻塞方式进行等待。

使用 :std::mutex

1.2 条件锁

条件锁就是所谓的条件变量,某一个线程因为某个条件为满足时可以使用条件变量使改程序处于阻塞状态。一旦条件满足以“信号量”的方式唤醒一个因为该条件而被阻塞的线程。最为常见就是在线程池中,起初没有任务时任务队列为空,此时线程池中的线程因为“任务队列为空”这个条件处于阻塞状态。一旦有任务进来,就会以信号量的方式唤醒一个线程来处理这个任务。
使用 :pthread_cond_t

1.3 自旋锁

个线程想要获取一个被使用的自旋锁,那么它会一致占用CPU请求这个自旋锁使得CPU不能去做其他的事情,直到获取这个锁为止,这就是“自旋”的含义。
使用 :spinlock_t

1.4 读写锁

写者:写者使用写锁,如果当前没有读者,也没有其他写者,写者立即获得写锁;
否则写者将等待,直到没有读者和写者。

读者:读者使用读锁,如果当前没有写者,读者立即获得读锁;否则读者等待,直到没有写者。

操作 函数说明
初始化读写锁 pthread_rwlock_init 语法
读取读写锁中的锁 pthread_rwlock_rdlock 语法
读取非阻塞读写锁中的锁 pthread_rwlock_tryrdlock 语法
写入读写锁中的锁 pthread_rwlock_wrlock 语法
写入非阻塞读写锁中的锁 pthread_rwlock_trywrlock 语法
解除锁定读写锁 pthread_rwlock_unlock 语法
销毁读写锁 pthread_rwlock_destroy 语法

1.5 递归锁

所谓递归锁,就是在同一线程上该锁是可重入的,对于不同线程则相当于普通的互斥锁。
使用规则:例如函数a需要获取锁mutex,函数b也需要获取锁mutex,同时函数a中还会调用函数b。如果使用std::mutex必然会造成死锁。但是使用std::recursive_mutex就可以解决这个问题。

使用 :std::recursive_mutex

2 锁的使用

2.1 基本使用:

  • 互斥锁:lock和unlock要对应,特别注意处理异常处理时,需要unlock释放锁
  • 多个线程锁:要保证多个互斥量上锁的顺序一样就不会造成死锁。即嵌套多个线程锁,要保证上锁一致
  • lock_guard类模板:构造函数进行加锁,析构函数解锁,采用作用域形式,保证加锁和解锁对应

2.2 死锁

死锁至少有两个互斥量mutex1,mutex2。

2.2.1 死锁产生的四个必要条件

  • 互斥条件:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。
  • 不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
  • 请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
  • 循环等待条件:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。

以上给出了导致死锁的四个必要条件,只要系统发生死锁则以上四个条件至少有一个成立。事实上循环等待的成立蕴含了前三个条件的成立,似乎没有必要列出然而考虑这些条件对死锁的预防是有利的,因为可以通过破坏四个条件中的任何一个来预防死锁的发生。

常规做法:

  • 保证多个互斥量上锁的顺序一样就不会造成死锁。

  • std::lock()函数模板:

    • std::lock(mutex1,mutex2……); 一次锁定多个互斥量(一般这种情况很少),用于处理多个互斥量。
    • 如果互斥量中一个没锁住,它就等着,等所有互斥量都锁住,才能继续执行。如果有一个没锁住,就会把已经锁住的释放掉(要么互斥量都锁住,要么都没锁住,防止死锁)
    • 使用std::lock,需要自己手动unlock 互斥锁
  • std::lock_guard的std::adopt_lock参数

    • std::lock_guard my_guard(my_mutex,std::adopt_lock);
      加入adopt_lock后,在调用lock_guard的构造函数时,不再进行lock();
    • std::lock 配合 lock_guard 和std::adopt_lock 不用手动unlock

c++多线程二 -- 锁

原文:https://www.cnblogs.com/lihaihui1991/p/14281117.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!