这个源代码怎么能造成死锁?

时间:2014-01-24 20:12:26

标签: c++ multithreading buffer mutex deadlock

以下代码是整数的缓冲区。我试图通过多个线程(让我们说3个线程:1个“消费者”和2个“生产者”)找到可能导致死锁的代码:

class OneBuf{
    Mutex m;
    CondVar cv;
    int buffer;
    bool full;

    void put (int data){
        m.lock();
        while(full) cv.wait(m);
        buffer = data;
        full = true;
        cv.signal();
        m.unlock();
    }

    int get(){
        int data;
        m.lock();
        while(!full) cv.wait(m);
        full = false;
        data = buffer;
        cv.signal();
        m.unlock();
        return data;
    }
    }

这是一个练习,我被要求给出一个导致死锁的3个线程(1个消费者和2个生产者)的例子。此外,练习声明如果我用cv.broadcast()(而不是cv.signal())替换第12和22行,我可以避免任何死锁。我希望有所帮助。

2 个答案:

答案 0 :(得分:0)

我认为你应该写一些类似的东西:

try {
  mutex.acquire();
  try {
    // do something
  } finally {
    mutex.release();
  }
} catch(InterruptedException ie) {
  // ...
}

为了更准确地回答你的问题,我认为首先调用get()方法时会发生死锁。因此,线程锁定互斥锁并进入无限循环(因为条件永远不会成立)。

答案 1 :(得分:0)

好的,这解释了一些事情。我仍然无法看到如何解锁这段代码。如果我告诉我能​​做什么,也许会有所帮助。

通常需要

broadcast(),因为signal()可能会错过一个线程,使其处于等待状态,直到另一个信号()最终唤醒它。虽然技术上不是死锁(有时它可以自行解决),但最好避免这种情况。

假设full不是bool,而是int,它有三种状态:true(1),false(0)和dumpable(-1),并且你有第三种方法dump():

void dump (void){
    m.lock();
    while(full!=-1) cv.wait(m);
    cerr<<buffer<<endl;
    full = 1;
    cv.signal();
    m.unlock();
}

和put()将full设置为1,但设置为-1(可转储)状态,以便执行的正常线性顺序为

buf.put(7);
buf.dump();
int val=buf.get();

然后假设多线程执行是这样的:

1)带有dump()的线程进入,发现full为0,进入等待状态。

2)一个带get()的线程进入,发现full为0,等待。

3)带有put()的线程进入,设置值,将full设置为-1,发送信号,退出解锁互斥锁。

4)get()赢得比赛,然后醒来。发现该值为-1而不是1,再次等待。它仍然解锁互斥锁,所以其他一些put()会再次发送信号,也许现在dump()会赢。或者可能不是。虽然不是僵局,但它很糟糕,并且很容易退化成几乎与真正的僵局无法区分的东西。

通过将signal()更改为broadcast()可以很容易地解决这个问题。如果put()发送broadcast(),则在4)中唤醒get()和dump()。他们仍在比赛,但这一次他们争夺互斥体。 get()仍然可以赢得比赛,但即使它确实如此,dump()也不会再等待(),它会一直停止,直到互斥锁被解锁,即直到get()检查完全!= 1然后去等待()解锁互斥锁。所以broadcast()确实修复了这个代码,每个现有的推杆都会找到一个转储器,最终会找到一个吸气剂。

这个例子是人为的,几乎是愚蠢的,但我希望它有助于说明通常的问题和解决方案。

回到您的代码,如果我们在等待池中同时获得getter和putter,则会发生这样的退出等待竞争。在这种情况下,get()可以很容易地从另一个get()的信号中唤醒,然后再次等待。信号将被遗漏,而丢失比赛的put()可以无限期地保持等待状态,就像在我的例子中一样。

但是如果游泳池只包含吸气剂或推杆,我看不出信号()是如何弄乱的:如果游泳池由吸气剂组成,任何其他吸气剂都会进入水池,任何推杆的信号都会找到一个醒来的吸气鬼。而我无法看到的是,吸气剂和推杆都可以用你的代码进入等待状态。即使比方说两个推杆竞争互斥锁,而两个吸气剂等待,赢家将唤醒一个吸气剂,这将保证继承其waker的互斥体。失败者推杆和从等待返回的吸气器之间没有比赛,等待的回归获胜。也许我想念一些东西。