整理mutex等互斥资源的常用用法

如果写多线程程序,那么需要通过加锁的方式来实现同步和互斥。c++标准库提供了mutex库。

参考链接:

  1. 菜鸟教程
  2. cpp reference

Mutex基本语法

mutex提供了下面的几个类

  1. std::mutex:基本的互斥锁
  2. std::recursive_mutex:递归互斥锁,允许同一个线程多次锁定。
  3. std::timed_mutex:具有超时功能的互斥锁。
  4. std::recursive_timed_mutex:具有超时功能的递归互斥锁。

mutex的基本用法

使用mutex基本语法获取锁,常用lock(),try_lock(),unlock()等函数进行操作。

  • lock()函数:当前线程获取互斥锁资源,如果互斥锁资源被其他线程占用,当前线程会被阻塞。
  • try_lock()函数:当前线程尝试获取互斥锁资源,如果获取资源成功,该函数返回true。如果获取资源失败,该函数返回false。
  • unlock()函数:释放当前互斥锁资源。

lock()

使用mutex的lock()函数去获取互斥锁。
通过执行结果时,能看到没有获取到锁资源的线程会阻塞等待前面的线程释放资源。

#include <iostream>
#include <thread>
#include <mutex>

int32_t gNumM = 0;
std::mutex mtx;

void incrementWithMutex(int32_t id)
{
    std::cout << "function IN, id=" << id << std::endl; 
    mtx.lock();

    std::cout << "lock get by" << id << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(3));   //长时间占用锁
    for(int32_t i=0; i<5000; i++)
    {
        gNumM++;
    }

    mtx.unlock();
    std::cout << "unlock get by" << id << std::endl;
}

int main()
{

    std::thread t1(incrementWithMutex, 1);
    std::thread t2(incrementWithMutex, 2);

    t1.join();
    t2.join();

    std::cout << gNumM << std::endl;
    return 0;
}

运行结果:

function IN, id=1
lock get by1
function IN, id=2
unlock get by1
lock get by2
unlock get by2
10000

try_lock()

使用try_lock()尝试获取资源锁,没有获取到锁的情况下,会返回false。不会阻塞。

#include <iostream>
#include <thread>
#include <mutex>

int32_t gNumM = 0;
std::mutex mtx;

void incrementWithMutex(int32_t id)
{
    std::cout << "function IN, id=" << id << std::endl; 
    if(mtx.try_lock())
    {
        std::cout << "lock get by" << id << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(3));   //长时间占用锁
        gNumM += 5000;
        mtx.unlock();
        std::cout << "unlock get by" << id << std::endl;
    }
}

int main()
{

    std::thread t1(incrementWithMutex, 1);
    std::thread t2(incrementWithMutex, 2);

    t1.join();
    t2.join();

    std::cout << gNumM << std::endl;
    return 0;
}

t2没有获得锁,直接return,没有被阻塞,等到一直取得锁。

function IN, id=1
lock get by1
function IN, id=2
unlock get by1
5000

unlock()

在使用lock()或try_lock()后,必须使用unlock释放锁

不释放锁的情况:

#include <iostream>
#include <thread>
#include <mutex>

int32_t gNumM = 0;
std::mutex mtx;

void incrementWithMutex(int32_t id)
{
    std::cout << "function IN, id=" << id << std::endl; 
    if(mtx.try_lock())
    {
        std::cout << "lock get by" << id << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(3));   //长时间占用锁
        gNumM += 5000;
        //mtx.unlock();
        //std::cout << "unlock get by" << id << std::endl;
    }
    std::cout << "function OUT, id=" << id << std::endl; 
}

int main()
{

    std::thread t1(incrementWithMutex, 1);
    t1.join();
    std::this_thread::sleep_for(std::chrono::seconds(5));

    std::thread t2(incrementWithMutex, 2);
    t2.join();

    std::cout << gNumM << std::endl;
    return 0;
}

t1已经执行完了,因为没有释放锁,t2无法获取到锁资源。

function IN, id=1
lock get by1
function OUT, id=1
function IN, id=2
function OUT, id=2
5000

同一线程对在没释放资源的情况下对同一个锁再加锁,会造成死锁。

#include <iostream>
#include <thread>
#include <mutex>

int32_t gNumM = 0;
std::mutex mtx;


void display()
{
    std::cout << "display IN" << std::endl; 
    mtx.lock();
    std::cout << "gNumM=" << gNumM << std::endl; 
    mtx.unlock();
}


void incrementWithMutex(int32_t id)
{
    std::cout << "function IN, id=" << id << std::endl; 
    if(mtx.try_lock())
    {
        std::cout << "lock get by" << id << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(3));   //长时间占用锁
        gNumM += 5000;

        display();

        
        mtx.unlock();
        std::cout << "unlock get by" << id << std::endl;
    }
    std::cout << "function OUT, id=" << id << std::endl; 
}



int main()
{

    std::thread t1(incrementWithMutex, 1);
    t1.join();

    std::cout << gNumM << std::endl;
    return 0;
}
function IN, id=1
lock get by1
display IN
->程序会卡死在这里

recursive_mutex

递归互斥锁,同一个线程可以多次锁定同一个互斥锁。

#include <iostream>
#include <thread>
#include <mutex>

int32_t gNumM = 0;
std::mutex mtx;
std::recursive_mutex rMtx;

void display()
{
    std::cout << "display IN" << std::endl; 
    rMtx.lock();
    std::cout << "gNumM=" << gNumM << std::endl; 
    rMtx.unlock();
}


void incrementWithMutex(int32_t id)
{
    std::cout << "function IN, id=" << id << std::endl; 
    if(rMtx.try_lock())
    {
        std::cout << "lock get by" << id << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(3));   //长时间占用锁
        gNumM += 5000;

        display();

        
        rMtx.unlock();
        std::cout << "unlock get by" << id << std::endl;
    }
    std::cout << "function OUT, id=" << id << std::endl; 
}



int main()
{

    std::thread t1(incrementWithMutex, 1);
    t1.join();

    std::cout << gNumM << std::endl;
    return 0;
}

使用recursive_mutex不会导致线程死锁

lock get by1
display IN
gNumM=5000
unlock get by1
function OUT, id=1
5000

timed_mutex和recursive_timed_mutex.

lock()和try_lock()用法和mutex以及recursive_mutex的用法一样。新增了try_lock_for()和try_lock_until()函数,如果在指定时间内无法获取互斥锁,则返回false。

#include <iostream>
#include <thread>
#include <mutex>

std::timed_mutex tMtx;
std::recursive_timed_mutex rtMtx;

int32_t gNum = 0;

void increase(int32_t id)
{
    std::cout << id << "increase in\n";
    if(tMtx.try_lock_for(std::chrono::seconds(3)))
    {
        std::cout << id << "locking\n";
        std::this_thread::sleep_for(std::chrono::seconds(5));
        gNum = 5000;
        tMtx.unlock();  
    }
    std::cout << id << "quit\n";  
}


int main()
{

    std::thread t1(increase, 1);
    std::thread t2(increase, 2);

    t1.join();
    t2.join();

    std::cout << gNum << std::endl;
    return 0;
}

线程2等待了3s就退出了,没有一直阻塞等待。

1increase in
1locking
2increase in
2quit
1quit
5000
posted @ 2025-04-07 17:42  酸菜馅粘豆包  阅读(111)  评论(0)    收藏  举报