c++多线程入门(3)

本文详细介绍了C++多线程编程中的关键概念,包括使用互斥量mutex保护数据、线程安全的容器stack实现、同时加多锁的策略、利用锁初始化数据(如单例模式)、读锁和写锁的运用,以及递归加锁的实现。强调了正确加锁和解锁的重要性,以及如何避免死锁和性能影响。
摘要由CSDN通过智能技术生成

一:用互斥量mutex保护受访问的数据

int i = 0;
mutex m;
void test()
{
    m.lock();//加锁
    i = i + 1;
    printf("i=%d\n", i);
    m.unlock();//释放锁
}
int main()
{
    thread t1(test);
    thread t2(test);
    t1.join();
    t2.join();
    printf("main thread: i=%d\n", i);
    return 0;
}

和thread要调用join类似, mutex也要确保调用了unlock, 否则另一个线程会无休止等待锁

类lock_guard可以确保该类析构时释放锁(和jthread类似)

这样test函数可写为

void test()
{
    //c++17支持类模板参数推导, c++17之前需要lock_guard<mutex> guard(m)
    //此外c++17还引入了scoped_lock, 是增强版的lock_gard, 也可scoped_lock guard(m)
    lock_guard guard(m);
    
    i = i + 1;
    printf("i=%d\n", i);
}

 二:多线程下的容器stack探讨

假如我们实现了一个用mutex保护的多线程stack

template<typename T>
class my_stack
{
    stack<T> s;
    mutex m;
public:
    uint32_t size()
    {
        lock_guard guard(m);
        return s.size();
    }

    bool empty()
    {
        lock_guard guard(m);
        return s.empty();
    }
    void push(T const&);
    T& top();
    void pop();
};

这种实现会有问题:

empty()和size()的结果不可信, 因为即使内部加锁保护, 但函数一旦返回后其他线程就不受限制, 可以随意添加或修改元素, 返回结果并不代表实时结果

考虑如下行为

    my_stack<int> s;
    if (!s.empty())
    {
        int value = s.top();
        s.pop();
    }

1, s.empty的判断结果会失效: 一个线程判断非空进入if语句块, 另一个线程马上pop掉最后一个元素, 此时该stack变空了, 在空stack上调用top会导致未定义行为

2, 一种可能的执行顺序

在此执行顺序下, 线程AB的value为同一个值, 然后各pop掉一个元素, 这样会导致pop的第二个元素未被读取就丢掉了

----

针对问题更改方案, 考虑把empty,pop和top组成一个函数, 加以保护, 如下

    T top()
    {
        lock_guard guard(m);
        if (!s.empty())
        {
            T value = s.top();
            s.pop();
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值