c++各种锁
·
#include<string>
#include<iostream>
#include<mutex>
#include<shared_mutex>
#include<thread>
#include<chrono>
using namespace std;
/*
std::mutex,基本的 mutex 类
std::recursive_mutex,支持同一个线程递归访问的 mutex 类
std::time_mutex,支持时间设定的 mutex 类
std::recursive_timed_mutex,支持递归和时间设定的 mutex 类
std::shared_mutex,允许独占和共享两个级别的锁 mutex 类(C++ 17 提供)
std::shared_timed_mutex,允许独占和共享两个级别的锁的带时间设定的 mutex 类(C++ 14 提供)
*/
void testMutex()
{
mutex mtx;
auto thfun = [&](int v){
for(int i=0;i<v;i++)
{
mtx.lock();//阻塞直到获取到锁,
//try_lock()用于尝试获取互斥锁,当取不到的时返回false,不会阻塞
// while(!mtx.try_lock())
// {
// std::this_thread::sleep_for(std::chrono::milliseconds(200));
// }
cout<<"threadid="<<std::this_thread::get_id()<<":"<<i<<endl;//io输出不加锁输出混乱
mtx.unlock();
}
};
thread t1(thfun, 3);
thread t2(thfun, 3);
thread t3(thfun, 3);
t1.join();
t2.join();
t3.join();
}
void testTimedMutex()
{
timed_mutex mtx;//相对于mutex多了,try_lock_for() 和 try_lock_until() 两个函数用于时间控制
auto thfun = [&](){
bool result = mtx.try_lock_for(chrono::seconds(2));//主线程已经锁住,这里会阻塞2s超时返回false
// bool result = mtx.try_lock_until(chrono::system_clock::now()+chrono::seconds(2));//等待直到当前时间2s后超时
cout<<std::this_thread::get_id()<<",result="<<result<<endl;
if(result)
{
this_thread::sleep_for(chrono::seconds(2));
mtx.unlock();
}
};
mtx.lock();//这里锁住
thread t1(thfun);
t1.join();
}
void testRecursiveMutex()
{
recursive_mutex rmtx;
int sum = 0;
auto sumfun = [&](int v){
for(int i=0;i<v;i++)
{
rmtx.lock(); //try_lock()与mutex一样,当取不到的时返回false,不会阻塞,但同一线程已经lock的也能正常获取到返回true
sum+=i;
rmtx.unlock();
}
};
auto thfun = [&](int v){
rmtx.lock();//第一次锁,
sumfun(v);//递归调用的场景,第二次锁,如果是mutex锁进入会直接死锁,recursive_mutex同一线程不会死锁
cout<<"threadid="<<std::this_thread::get_id()<<":"<<sum<<endl;
rmtx.unlock();
};
thread t1(thfun, 3);
t1.join();
}
void testTimeRecursiveMutex()
{
recursive_timed_mutex rmtx;//相对于recursive_mutex多了,try_lock_for() 和 try_lock_until() 两个函数用于时间控制
int sum = 0;
auto thfun = [&](){
bool result = rmtx.try_lock_for(chrono::seconds(2));//主线程已经锁住,这里会阻塞2s超时返回false
cout<<std::this_thread::get_id()<<",result="<<result<<endl;
result = rmtx.try_lock_for(chrono::seconds(2));//主线程已经锁住,这里会阻塞2s超时返回false
cout<<std::this_thread::get_id()<<",result="<<result<<endl;
cout<<"threadid="<<std::this_thread::get_id()<<":"<<sum<<endl;
rmtx.unlock();
rmtx.unlock();
};
rmtx.lock();//这里锁住
thread t1(thfun);
t1.join();
}
void testSharedMutex()
{
//std::shared_mutex 设置了两种级别的锁控制,一种是独占锁控制,一种是共享锁控制,对于写操作,用独占锁,对于读操作用共享锁
//独占锁控制的方法是:lock()、unlock() 和 try_lock() 方法,它们的行为和 mutex 是一样的
//共享锁控制的方法是:lock_shared()、try_lock_shared() 和 unlock_shared()
//一个 shared_mutex,如果被 lock() 方法独占锁了,那么其他线程再对这个互斥体无论是 lock(),还是 lock_shared(),都将阻塞,直到互斥体被 unlock
//如果是被lock_shared()上锁,则其他线程中的lock_shared()不会阻塞,如果其他线程是 lock()则会被挂起,直到所有获取共享锁的线程都unlock_shared()才能继续上独占锁
std::shared_mutex smutex;
auto reader = [&]() {
smutex.lock_shared();
std::cout << this_thread::get_id()<<",read" << std::endl;
smutex.unlock_shared();
};
auto writer = [&]() {
smutex.lock();
std::cout << "write" << std::endl;
this_thread::sleep_for(chrono::seconds(1));
smutex.unlock();
};
int count = 8;
std::thread rw_threads[count];
for(int i = 0; i < count; i++) {
if((i % 4) == 0)
rw_threads[i] = std::thread(writer);
else
rw_threads[i] = std::thread(reader);
}
for(auto& t : rw_threads)
t.join();
}
void testSharedTimedMutex()
{
//std::shared_timed_mutex相对于std::shared_mutex,分别多了可以设置超时的方法
//独占锁控制的方法多了:try_lock_for() 和 try_lock_until()
//共享锁控制的方法多了:try_lock_shared_for() 和 try_lock_shared_until()
std::shared_timed_mutex smutex;
auto reader = [&]() {
smutex.try_lock_shared_for(chrono::seconds(3));
std::cout << this_thread::get_id()<<",read" << std::endl;
smutex.unlock_shared();
};
auto writer = [&]() {
smutex.try_lock_for(chrono::seconds(3));
std::cout << "write" << std::endl;
this_thread::sleep_for(chrono::seconds(1));
smutex.unlock();
};
int count = 8;
std::thread rw_threads[count];
for(int i = 0; i < count; i++) {
if((i % 4) == 0)
rw_threads[i] = std::thread(writer);
else
rw_threads[i] = std::thread(reader);
}
for(auto& t : rw_threads)
t.join();
}
/*
五种类型的守护锁分别是:
std::lock_guard,方便线程对各种 mutex 互斥量上锁
std::unique_lock,方便线程对各种 mutex 互斥量上锁,提供更灵活的控制手段
std::shared_lock,配合 shared mutex 互斥量(C++ 14)
std::scoped_lock,方便线程对各种 mutex 互斥量上锁(C++ 17)
*/
void test_lock_guard()
{
/*
std::lock_guard:
1.构造时自动lock()
2.析构时自动unlock()
3.禁止拷贝构造和赋值操作(不能复制或移动)
4.带adopt_lock参数的构造函数,不会在构造时加锁,通常用于当互斥锁已经被当前线程加锁,但需要lock_guard在作用域结束时自动解锁的情况
5.构造时只能lock(),无法使用try_lock、lock_shared、try_lock_shared_for等上锁
*/
{
std::mutex amtuex;
std::lock_guard<std::mutex> guard(amtuex);
std::mutex amtuex2;
amtuex2.lock();
std::lock_guard<std::mutex> guard1(amtuex2,adopt_lock);//adopt_lock参数构造时不会在构造时加锁,但析构时自动解锁
std::recursive_mutex rmutex;
std::lock_guard<std::recursive_mutex> guard2(rmutex);
std::timed_mutex tmutex;
std::lock_guard<std::timed_mutex> guard3(tmutex);
std::recursive_timed_mutex rtrmutex;
std::lock_guard<std::recursive_timed_mutex> guard4(rtrmutex);
std::shared_mutex smutex;
std::lock_guard<std::shared_mutex> guard5(smutex);
std::shared_timed_mutex stmutex;
std::lock_guard<std::shared_timed_mutex> guard6(stmutex);
}
}
void test_unique_lock()
{
/*
默认:构造时调用 mutex.lock()(与 lock_guard 一致);
std::defer_lock:延迟上锁(构造时不上锁,后续手动调用 lock());
std::try_to_lock:尝试上锁(成功返回 true,失败不阻塞,需通过 owns_lock() 判断);
std::adopt_lock:适配已上锁的互斥量(与 lock_guard 一致)
*/
//默认与 lock_guard 一致
{
std::mutex amtuex;
std::unique_lock<std::mutex> unique(amtuex);
std::mutex amtuex2;
amtuex2.lock();
std::unique_lock<std::mutex> unique1(amtuex2,adopt_lock);//adopt_lock参数构造时不会在构造时加锁,但析构时自动解锁
std::recursive_mutex rmutex;
std::unique_lock<std::recursive_mutex> unique2(rmutex);
std::timed_mutex tmutex;
std::unique_lock<std::timed_mutex> unique3(tmutex);
std::recursive_timed_mutex rtrmutex;
std::unique_lock<std::recursive_timed_mutex> unique4(rtrmutex);
std::shared_mutex smutex;
std::unique_lock<std::shared_mutex> unique5(smutex);
std::shared_timed_mutex stmutex;
std::unique_lock<std::shared_timed_mutex> unique6(stmutex);
}
std::mutex mtx;
{
std::unique_lock<std::mutex> lock(mtx);
// std::unique_lock<std::mutex> lock2(lock);//unique_lock不可复制,但可通过std::move() 转移所有权
std::unique_lock<std::mutex> lock2(std::move(lock));
}
{
// 延迟上锁:构造时不上锁
std::unique_lock<std::mutex> lock(mtx, std::defer_lock);
// 手动上锁(此时才真正获取锁)
lock.lock();//unique_lock的lock会先调用锁的lock然后设置owns = true,所以其析构时能自动解锁
// 手动解锁(提前释放锁,提高并发效率)
lock.unlock();
// 可多次调用
lock.lock();
// 手动解锁(不手动调用,其能析构时也能自动解锁)
lock.unlock();
}
{
//mtx.lock();构造前加上锁,try_to_lock就会失败,不能成功获取锁owns_lock返回false,此时析构时不会自动解锁
std::unique_lock<std::mutex> lock(mtx, std::try_to_lock);
if (lock.owns_lock()) { // 判断是否成功获取锁
std::cout << "Successfully locked, access shared resource"<<endl;
} else {
std::cout << "Failed to lock, do other things"<<endl;
}
// 析构时(若持有锁,即owns_lock为true)才会自动解锁,否则不会自动解锁
}
{
mtx.lock();
//适配已上锁的互斥量(与 lock_guard 一致),adopt_lock参数构造时不会在构造时加锁,但析构时自动解锁
std::unique_lock<std::mutex> lock(mtx, std::adopt_lock);
std::cout << "adopt_lock"<<endl;
//析构时会自动解锁
}
/*
配合条件变量condition_variable使用
std::condition_variable 的 wait() 必须传入 unique_lock,因为 wait() 会:
自动解锁 unique_lock 对应的互斥量;
阻塞当前线程,等待唤醒;
被唤醒后,自动重新上锁 unique_lock
*/
{
std::mutex mtx;
std::condition_variable cv;
bool ready = false; // 共享条件
auto worker = [&]() {
std::unique_lock<std::mutex> lock(mtx);
std::cout << "Worker start"<<endl;
// 等待条件满足(自动解锁,阻塞;被唤醒后重新上锁)
cv.wait(lock, [&]{ return ready; });//wait()必须传入 unique_lock
// 条件满足,执行临界区
std::cout << "Worker"<<endl;
};
std::thread t(worker);
{
this_thread::sleep_for(chrono::seconds(1));//使wait先执行,解锁,等待唤醒,配合后面测试枷锁后notify_one,浮现wait卡死现象
std::lock_guard<std::mutex> lock(mtx); // 简单场景用 lock_guard 即可
ready = true;
} // 析构时解锁
//std::lock_guard<std::mutex> lock(mtx);这里如果再上锁,wait里面被唤醒后无法重新lock导致卡死
std::cout<<"notify_one"<<endl;
cv.notify_one(); // 唤醒等待的线程
t.join();
}
}
void test_shared_lock()
{
/*
1.只能用于管理共享锁shared_mutex,shared_timed_mutex
2.析构时如果持有锁会调用unlock_shared()
*/
{
std::shared_mutex smutex;
std::shared_lock<std::shared_mutex> share(smutex);//构造时默认调用smutex.lock_shared()
std::shared_timed_mutex stmutex;
std::shared_lock<std::shared_timed_mutex> share1(stmutex);
//std::shared_lock<std::shared_mutex> lock2(share);//shared_lock不可复制,但可通过std::move() 转移所有权
std::shared_lock<std::shared_mutex> share2(std::move(share));
}
std::shared_mutex smutex; // reader/writer mutex
{
// 延迟上锁:构造时不上锁
std::shared_lock<std::shared_mutex> lock(smutex, std::defer_lock);//构造时不调用任何lock函数,owns_lock===false
// 手动上锁(此时才真正获取锁)
lock.lock();//lock会先调用锁的shared_lock然后设置owns = true,所以其析构时能自动解锁
// 手动解锁(不手动调用,其能析构时也能自动解锁)
lock.unlock();
}
{
// smutex.lock();//构造前加上独占锁,try_to_lock就会失败,不能成功获取锁owns_lock返回false,此时析构时不会自动解锁
// smutex.lock_shared();//构造前加上非独占锁,try_to_lock就会成功获取锁owns_lock返回ture,此时析构时不会自动解锁
std::shared_lock<std::shared_mutex> lock(smutex, std::try_to_lock);//构造时调用try_lock_shared
if (lock.owns_lock()) { // 判断是否成功获取锁
std::cout << "Successfully locked, access shared resource"<<endl;
} else {
std::cout << "Failed to lock, do other things"<<endl;
}
// 析构时(若持有锁,即owns_lock为true)才会自动解锁,否则不会自动解锁
}
{
smutex.lock_shared();//这里不调用smutex.lock(),否则shared_lock析构无法自动解锁
//适配已上共享锁的互斥量,adopt_lock参数构造时不会在构造时加锁,但析构时自动unlock_shared()解锁
std::shared_lock<std::shared_mutex> lock(smutex, std::adopt_lock);
std::cout << "adopt_lock"<<endl;
//析构时(若持有锁,即owns_lock为true)会自动解锁,unlock_shared()
}
std::shared_timed_mutex stmutex; // reader/writer mutex
{
auto time = chrono::system_clock::now()+chrono::seconds(5);
std::shared_lock<std::shared_timed_mutex> lock(stmutex,time);//构造时调用try_lock_shared_until
std::cout << "try_lock_shared_until"<<endl;
//析构时(若持有锁,即owns_lock为true)会自动解锁,unlock_shared()
}
{
auto dur = chrono::seconds(5);
//适配已上共享锁的互斥量,adopt_lock参数构造时不会在构造时加锁,但析构时自动unlock_shared()解锁
std::shared_lock<std::shared_timed_mutex> lock(stmutex, dur);//构造时调用try_lock_shared_for
std::cout << "try_lock_shared_for"<<endl;
//析构时会自动解锁,unlock_shared()
}
}
void test_scoped_lock()
{
/*
支持同时持有多个互斥量(可以是不同类型)并在构造时一次性、无死锁地上锁,离开作用域自动按逆序解锁
1.构造时自动(内部算法动态决定多个互斥量的锁定顺序)lock(),不会出现部分锁定状态:要么同时锁定所有互斥锁,要么都不锁定
2.析构时自动加锁顺序的逆序unlock()
3.禁止拷贝构造和赋值操作(不能复制或移动)
4.构造时若上锁过程中抛出异常,已获取的锁会被释放(std::lock 保证),对象构造失败不泄露锁
5.切记:传入同一个互斥量会自锁死
*/
std::mutex mutex;
std::mutex mutex2;
std::recursive_mutex rmutex;
std::shared_mutex smutex;
{
//构造时按照内部算法决定顺序对mutex,rmutex,smutex进行lock
std::scoped_lock<std::mutex,std::recursive_mutex,std::shared_mutex> scoped(mutex,rmutex,smutex);
//析构时按照上锁顺序的逆序进行unlock
}
{
mutex2.lock();
cout<<"lock ok"<<endl;
thread t([&](){
this_thread::sleep_for(chrono::seconds(2));
mutex2.unlock();
});
//锁定mutex2失败时,此时mutex已被锁定,它会自动释放mutex,进入短暂的等待状态,再次尝试同时锁定两个互斥锁,重复此过程,直到成功锁定所有互斥锁或发生异常
std::scoped_lock<std::mutex,std::mutex> scoped(mutex,mutex2);//会阻塞2s,直到mutex释放锁,且在
cout<<"scoped_lock ok"<<endl;
t.join();
}
/*
1.锁定顺序不是按照参数传入顺序,而是采用一种特殊的死锁避免算法来确定锁定顺序
2.确保所有线程按照相同的逻辑顺序加锁,从而从根本上防止死锁发生
{
std::mutex mtx1, mtx2, mtx3;
thread t([&](){
cout<<"t1"<<endl;
std::scoped_lock<std::mutex,std::mutex,std::mutex>(mtx3, mtx2, mtx1);//不会产生死锁
cout<<"t1 lock"<<endl;
});
thread t2([&](){
cout<<"t2"<<endl;
std::scoped_lock<std::mutex,std::mutex,std::mutex>(mtx1, mtx2, mtx3);//不会产生死锁
cout<<"t2 lock"<<endl;
});
t.join();
t2.join();
}
*/
}
int main()
{
testMutex();
testTimedMutex();
testRecursiveMutex();
testTimeRecursiveMutex();
testSharedMutex();
testSharedTimedMutex();
test_lock_guard();
test_unique_lock();
test_shared_lock();
test_scoped_lock();
return 0;
}
更多推荐


所有评论(0)