单一锁的死锁问题

当只有一个锁时,死锁可能因消费者获取锁后不释放导致。通过生产者先解锁,利用条件变量进行等待与通知,可以避免死锁。消费者在资源未准备好时解锁并等待,生产者在资源准备好时唤醒条件变量,从而协调两者,确保资源的正确使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于只有一个锁的情况,死锁发生在消费者获取锁之后一直不释放。如果每次生产者先于消费者拿到锁,譬如先启动生产者,然后等待一段时间后再去启动消费者,这个死锁是可以避免的。但是真实的情况,生产者和消费者的启动时机是内核负责调度的,谁先谁后系统没有保证。所以要考虑消费者先于生产者运行的情形该怎么避免死锁。

而解决死锁的本质在于,要给生产者线程进入临界区域的时机(一般来说,消费者死锁很容易避免)。解决方式有两种:
一种是,消费者检查到资源没有准备好之后,unlock,等待一段时间,再次获取锁,重新检查资源是否准备好;如果资源准备好了,就直接unlock返回了。资源没有准备好的模式是: unlock, wait(time),lock。
另一种是在方法一基础上做了改进,就是消费者检查到资源没有准备好,unlock之后,用Condition variable 进行等待。方法一等待的时间是用户指定的。而这里等待的时间,取决于生产者什么时候唤醒这个Condition variable。Condition variable被唤醒后,会再次去lock,然后检查资源是否准备好。资源没有准备好的模式是::unlock, wait(signal),lock。

死锁问题

死锁发生在消费者先获得锁,然后不停的检查条件是否为真。与此同时,生产者会被调度出去,也没有机会把消费者等待的条件修改为真。所以,这个时候,消费者一直在检查条件是否为真,生产者一直处于睡眠状态。两者形成死锁。

void ThreadConsumer() {
  unique_lock<mutex> lock(g_mutex);

  while (count == 0) {
  }
}

void ThreadProducer() {
  // In producer thread count++:
  lock_guard<mutex> lock(g_mutex);
  count++;
}

解决方案: unlock wait lock

前面提到,解决死锁的关键是给生产者获取锁的机会。第一种避免死锁的逻辑是,在等待条件为真(等待生产者把数据准备好)的while循环里面,先unlock。这个unlock给了消费者的lock和生产者的lock一个机会。至于消费者和生产者谁先得到这个机会,要看内核代码(我猜测内核应该优先把这个机会给本线程之外的其他线程获取这个锁)。假设消费者先得到,那么生产者仍然没有机会去修改状态。这个时候,程序需要做一次没有意义的循环,但是经过多次尝试,生产者总会有机会拿到这个锁的(除非内核非常偏心,每次都把消费者释放的锁先交给消费者)。为了增加生产者先拿到消费者释放的这个锁的概率,可以让消费者检查到资源没准备好之后,释放锁,然后等待(即使内核偏心消费者,但是消费者睡着了,你总该给生产者一个机会吧):

void ThreadConsumer() {
  unique_lock<mutex> lock(g_mutex);
  while (count == 0) {
    // g_mutex.unlock();
    lock.unlock();
    // g_mutex.lock();
    // wait here.
    lock.lock();
  }
}

void ThreadProducer() {
  // In producer thread count++:
  lock_guard<mutex> lock(g_mutex);
  count++;
}

总的来说,unlock/lock可以避免死锁。但是这不是完美的解决方案。等待,没有通知好。

解决方案: condition variable

消费者Std::condition_variable::wait执行了下面这些操作:

Unlock
Wait
Lock

生产者则负责了通知std::condition_variable::notify_one

和前面的unlock/lock方案相比,condition variable引入了Wait+ notify的动作。其意义在于,之前unlock/lock方案,在消费者unlock之后,需要等待一个不确定的时间,这个时间过短,资源没有准备好。过长,则影响了效率。而在引入了Wait+ notify,消费者 unlock之后,需要等待生产者的notify之后才去尝试获取lock。所以,生产者的Notify动作,可以相对精确的告知消费者什么时候结束等待。

void ThreadConsumer() {
  unique_lock<mutex> lock(g_mutex);
  while (count == 0) {
    cv.wait(lock);
  }
}

void ThreadProducer() {
  // In producer thread count++:
  lock_guard<mutex> lock(g_mutex);
  count++;
  cv.notify_one();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值