首页 > 编程知识 正文

同步信号量和互斥信号量,二值信号量和互斥信号量

时间:2023-05-05 02:49:02 阅读:110705 作者:1965

文章目录Mutex互斥量(互斥锁)概要1.Mutex系列类) 4种)std:mutex )此类表示普通互斥锁,不能递归使用。 std:recursive_mutex :此类表示递归(再入)互斥锁。 递归独占锁可以在同一线程中多次锁定,以获得对互斥锁对象的多层所有权。 std:time_mutex :此类表示计时器(有限时间)独占锁,不能递归使用。 STD :3360 recursive _ timed _ mutex :带计时器的递归互斥。 2.Lock类(四种)一:lock_guard (锁定保护(二) scoped_lock )范围)三) unique_lock (独占,唯一)四) shared_lock

Mutex排他量(排他锁)简介

C11标准库头文件中定义了独占访问的类和方法等。 mutex也称为排他量。

1.Mutex系列类(四种)std:mutex )此类表示普通互斥锁,不能递归使用。 std:mutex是C 11最基本的互斥量,用于保护共享数据免受多个线程同时访问的同步原语的影响。STD:3360mutex对象提供独占(独占)所有权的特性,不支持递归锁定STD33603360mutex对象

std::mutex 的成员函数

(1).构造函数和析构函数:

std::mutex 不允许拷贝构造,也不允许 move 拷贝,第一个生成的mutex对象处于未锁定状态。 std:~mutex )放弃互斥锁。 如果独占锁被线程占用,或者线程在拥有独占锁的所有权时终止,则不会定义行为。

(2)加锁函数:lock(),调用线程锁定此排他量。 当线程调用此函数时,会出现以下三种情况:

.如果当前未锁定此排他量,则调用线程将锁定此排他量,并且在调用unlock之前,线程将一直保持锁定状态。

如果当前排他量被其他线程锁定,则当前调用线程将被阻止。

如果当前独占量被当前调用线程锁定,则会发生死锁(deadlock )。

(3) 尝试加锁 try_lock()尝试锁定排他量,如果排他量被其他线程占用,则当前线程也不会阻塞。 线程调用此函数也有以下三种情况:

当前排他量未被其他线程占用时,在该线程调用unlock释放排他量之前,该线程将锁定排他量。

如果当前排他量被其他线程锁定,当前调用的线程将返回false而不会被阻塞。

当前的排他量被当前的调用线程锁定时,会发生死锁(deadlock )

(4)解锁函数: unlock(),解除锁定,释放对互斥量的所有权。

std:mutex g_mtx; //创建程序的执行,开始全局生存期。 voidthread_fun({g_MTX.lock ); //所有权//g_mtx.unlock (; //释放所有权(} std:recursive_mutex )此类表示递归(再入)独占锁。 递归独占锁可以在同一线程中多次锁定,以获得对互斥锁对象的多层所有权。该类表示递归(再入)互斥锁。递归互斥锁可以被同一个线程多次加锁,以获得对互斥锁对象的多层所有权例如,如果同一个线程的多个函数访问临界区域,则可以分别锁定它们,然后在执行后分别解除锁定。 std:recursive_mutex要释放互斥量,必须调用与该锁定级别深度相同次数的unlock ()。 也就是说,lock ) )次数和unlock ) )次数相同。 这样,当线程申请递归排他锁时,如果该递归排他锁已经被当前调用线程锁定,就不会发生死锁。 此外,std:recursive_mutex的功能与std:mutex基本相同。

std:time_mutex :此类表示计时器(有限时间)独占锁,不能递归使用。std::timed_mutex 比 std::mutex 多了两个成员函数:

)1) try_lock_for ) )函数参数表示线程未锁定时保持阻塞的时间范围。 如果在此期间其他线程解锁,则该线程可以获得互斥锁。 如果超时(在指定的时间范围内没有获取锁),函数调用将返回false。

)2) try_lock_until ) :函数自变量表示某个时刻,在该时刻之前如果线程没有获得锁,则保持阻塞状态; 如果到目前为止其他线程解除了锁定,则该线程可以获得互斥锁。 如果超过指定时间未获得锁,则函数调用返回false

std:timed_mutex mtx; //(有限时间锁定) voidfireworks(charch ) { while (! MTX.try _ lock _ for (STD :3360 chrono : milliseconds (200 ) ) )//如果在200毫秒内无法获得锁,则返回false {

std::cout << ch; } std::this_thread::sleep_for(std::chrono::milliseconds(1000)); std::cout << "*n"; mtx.unlock(); } int main() { const int n = 3; std::thread threads[n]; char ch = 'A'; // spawn n threads: for (int i = 0; i < n; ++i) { threads[i] = std::thread(fireworks, ch + i); } for (auto& th : threads) { th.join(); } return 0;} ④ std::recursive_timed_mutex:带定时的递归互斥锁。 2.Lock 类(四种) 一 :lock_guard (锁保护):

lock_guard 类不可复制(拷贝和赋值)。只有构造和析构函数。
类 lock_guard 是互斥体包装器,为在作用域块期间占有互斥提供便利 RAII 风格机制。创建 lock_guard 对象时,它试图接收给定互斥的所有权。控制离开创建 lock_guard 对象的作用域时,销毁 lock_guard 并释放互斥。不可用在函数参数传递或者返回过程,只能用在简单的临界区代码段的互斥操作中

std::mutex g_mtx; void threadfunc(char ch) { //首先,这在一个局部作用域内, std::lock_guard 在构造时, //会调用 g_mutex->lock() 方法; std::lock_guard<std::mutex> lock(g_mtx); for (int i = 0; i < 5; ++i) { std::lock_guard<std::mutex> lock(g_mtx); //加锁 for (int j = 0; j < 5; ++j) { // std::lock_guard<std::mutex> lock(g_mtx); printf("%c", ch); } printf("n"); } printf("n"); //局部作用域代码结束后, //std::lock_guard 的析构函数会被调用, //函数中会调用 g_mutex->unlock() 方法。} int main() { char ch = 'a'; std::thread that[5]; for (int i = 0; i < 5; ++i) { that[i] = std::thread(threadfunc, ch + i); } for (int i = 0; i < 5; ++i) { that[i].join(); } cout << "Main End" << endl; return 0; } 二: scoped_lock (范围):

scoped_lock 类不可复制(拷贝和赋值)。只有构造和析构函数。
类 scoped_lock 是提供便利 RAII 风格机制的互斥包装器,它在作用域块的存在期间占有一或多个互斥。创建scoped_lock 对象时,它试图取得给定互斥的所有权。控制离开创建 scoped_lock 对象的作用域时,析构 scoped_lock 并以逆序释放互斥。若给出数个互斥,则使用免死锁算法,如同以 std::lock 。

三:unique_lock (独占,唯一),

有锁定,修改和观察器函数。
类 unique_lock 是通用互斥包装器,允许延迟锁定、锁定的有时限尝试、递归锁定、所有权转移和条件变量一同使用。类 unique_lock 可移动,但不可复制——它满足可移动构造 (MoveConstructible) 和可移动赋值 (MoveAssignable) 但不满足可复制构造 (CopyConstructible) 或可复制赋值 (CopyAssignable) 。他不仅可以使用在简单的临界区代码段的互斥操作中,还能用在函数调用过程中

std::unique_lock<std::timed_mutex> lock; 四:shared_lock (共享),

有锁定,修改和观察器函数。
类 shared_lock 是通用共享互斥所有权包装器,允许延迟锁定、定时锁定和锁所有权的转移。锁定 shared_lock ,会以共享模式锁定关联的共享互斥( std::unique_lock 可用于以排他性模式锁定)。
shared_lock 类可移动,但不可复制——它满足可移动构造 (MoveConstructible) 与可移动赋值 (MoveAssignable) 的要求,但不满足可复制构造 (CopyConstructible) 或可复制赋值 (CopyAssignable) 。共享所有权模式等待于共享互斥,可使用 std::condition_variable_any ( std::condition_variable 要求std::unique_lock 故而只能以唯一所有权模式等待)。

3.互斥对象管理类模板的加锁策略

std::lock_guard、scoped_lock,std::unique_lock 和 std::shared_lock 类模板在构造时是否加锁是可选的,
C++11 提供了 3 种加锁策略。

下表列出了互斥对象管理类模板对各策略的支持情况。

死锁

死锁是指两个或两个以上的进程(或线程)在运行过程中因争夺资源而造成的一种僵局(Deadly-Embrace) ) ,若无外力作用,这些进程(线程)都将无法向前推进。

std::mutex mtxa;std::mutex mtxb;int countA = 1000;int countB = 1000;void thread_func1(int money) // A ——-- > B{std::scoped_lock lk(mtxa, mtxb); //同时获得两把锁来解决死锁问题//unique_lock<std::mutex> lca(mtxa); //单用该锁会造成死锁cout << "线程1获得A账户的锁" << endl;std::this_thread::sleep_for(std::chrono::milliseconds(1000));if (countA >= money){cout << "线程1将要获得B账户的锁" << endl;//unique_lock<std::mutex> lcb(mtxb); //单用该锁会造成死锁countA -= money;countB += money;cout << "转账成功A --—-- > B: " << money << endl ;}else {cout << "A账号余额不足" << endl;}}void thread_func2(int money) // A ——-- > B{std::scoped_lock lk(mtxa, mtxb); //同时获得两把锁来解决死锁问题//unique_lock<std::mutex> lca(mtxb); //单用该锁会造成死锁cout << "线程2获得B账户的锁" << endl;std::this_thread::sleep_for(std::chrono::milliseconds(1000));if (countB >= money){cout << "线程2将要获得A账户的锁" << endl;//unique_lock<std::mutex> lcb(mtxa); //单用该锁会造成死锁countB -= money;countA += money;cout << "转账成功B --—-- > A: " << money << endl;}else {cout << "B账号余额不足" << endl;}}int main(){thread t1(thread_func1, 200);thread t2(thread_func2, 500);t1.join();t2.join();return 0;} 死锁产生的 4 个必要条件

1、互斥: 某种资源一次只允许一个 线程 访问,即该资源一旦分配给某个 线程 ,其他 线程 就不能再访问,直到该 线程 访问结束。

2、占有且等待: 一个 线程 本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他 线程 释放该资源。

3、不可抢占: 线程已获得的资源,在末使用完之前,不能强行剥夺。

4、循环等待: 存在一个线程链,使得每个线程都占有下一个线程所需的至少一种资源。

当以上四个条件均满足,必然会造成死锁,发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致 CPU 的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机的使用性能的。那么,解决死锁问题就是相当有必要的了。

避免死锁的方法

a 、破坏“占有且等待”条件
b 、破坏“不可抢占”条件
c 、破坏“循环等待”条件

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。