首页 > 解决方案 > 是“僵局”吗?如何在多线程中避免它?

问题描述

我目前正在学习 C++ 11 的并发性。我为课程作业编写了一段代码。但是,代码没有完成运行。

我的环境:

系统:Windows 10

编译器:mingw-w64x86_64-8.1.0-posix-seh-rt_v6-rev0

更新:

我认为“加入”可以结束一个线程。感谢那些评论这个问题的好人,我明白“加入”不会“杀死”线程。它是线程执行完成时返回的函数。此外,我的代码中的问题可能与死锁有关。我已经尝试了一些方法来解决这个问题。但是,它都不起作用。

我尝试过的:添加

if(ConsumerPtr->isConsumerDead()){
            break;
        }

void Producer::prods();函数中。那是行不通的。后来我再编辑一些。我添加了一个新的全局变量来检查消费者是否停止工作。另外,我添加了更多 if 语句来检查消费者void Buffer::put();。在那之后,我发现代码很少工作,但大部分时间都失败了。我试图 std::this_thread::sleep_for(std::chrono::milliseconds(60));在单次消费/生产操作后编辑睡眠时间的代码。我已将 10 毫秒扩大到 60 毫秒。然后,代码工作得更好。现在,它很少失败。

我认为代码没有完成,因为在运行过程中出现了死锁。我在 Wiki 上找到了一个可能导致死锁的原因:当一个进程或线程进入等待状态时会发生死锁,因为请求的系统资源被另一个等待进程持有,而另一个等待进程又在等待另一个等待进程持有的另一个资源。如果一个进程不能无限期地改变它的状态,因为它请求的资源正被另一个等待的进程使用,那么系统就被称为死锁。

那么,问题就变成了:真的是死锁吗?如何避免死锁?我应该编辑什么以使我的代码更好?

这是新代码:

#include <iostream>
#include <thread>
#include <mutex>          // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable

using namespace std;

std::mutex mu; //mutex
//std::unique_lock<mutex> locker(mu); //unique_lock
std::condition_variable cond;

std::mutex m_mutex;

int consumerRunTurns = 0;

class Buffer {
public:
Buffer():count(0) {}
void put();
int get();
bool isBufferEmpty();
bool isBufferFull();
private:
int count;
}; //end class Buffer

//Thread Class and Functions inside:
class Consumer{
public:
    Consumer(Buffer* cbptr);
    void cons();
    bool isConsumerDead();
private:
    Buffer* BufferPtr;
};

class Producer{
public:
    Producer(Buffer* pbptr, Consumer* coptr);
    void prods();
private:
    Buffer* BufferPtr;
    Consumer* ConsumerPtr;
};

int main(){
    Buffer* tc = new Buffer();
    Consumer* cdtc = new Consumer(tc);
    Producer* pdtc = new Producer(tc,cdtc);
    std::thread t[5]; //declare an array of thread objects
    int i = 0;
    for(; i<3; ++i){
        t[i]=std::thread(&Producer::prods,std::ref(pdtc));
    }
    for(; i<5; ++i){
        t[i]=std::thread(&Consumer::cons,std::ref(cdtc));
    }

    cout << "Debug1" << endl;
    cout << "Debug1" << endl;
    cout << "Debug1" << endl;

    for(i = 0; i<5; ++i){
        t[i].join();
        cout << "now i is :" << i << endl;
    }

    cout << "All threads terminated" << endl;
    return 0;
}

void Buffer::put(){
    std::unique_lock<mutex> locker(mu); //unique_lock
    if(consumerRunTurns>=200){
            cond.notify_one();
            return;
            }
    while(isBufferFull()==1){
        if(consumerRunTurns>=200){
            cond.notify_one();
            break;
        }
        cond.wait(locker);
    }
    if(consumerRunTurns>=200){
            return;
    }
    if(isBufferFull()==0)
        cond.notify_one();
    ++count;
    std::cout << "producer thread" << this_thread::get_id() << ",count = " << count << endl;
}
int Buffer::get(){
    std::unique_lock<mutex> locker(mu); //unique_lock
    while(isBufferEmpty()==1){
        cond.wait(locker);
    }
    if(isBufferEmpty()==0)
        cond.notify_one();
    --count;
    std::cout << "consumer thread" << this_thread::get_id() << ",count = " << count << endl;
    return count;
}
bool Buffer::isBufferEmpty(){
    if(count <=0){
        std::cout << "buffer is empty, consumer thread" << this_thread::get_id() << " is about to suspend." << endl;
        return 1;
    }
    else
        return 0;
}
bool Buffer::isBufferFull(){
    if(count >=10){
        std::cout << "buffer is full, producer thread" << this_thread::get_id() << " is about to suspend." << endl;
        return 1;
    }
    else
        return 0;
}

Consumer::Consumer(Buffer* cbptr):BufferPtr(cbptr){
}

bool Consumer::isConsumerDead(){
    if(consumerRunTurns >= 200){
        cout << "consumer Dead" << endl;
        cout << "consumer Dead" << endl;
        cout << "consumer Dead" << endl;
        return 1;
    }
    else
        return 0;
}

void Consumer::cons(){
    for(int i = 0; i<100; ++i){
        BufferPtr->get();
        std::this_thread::sleep_for(std::chrono::milliseconds(60));
    }
    consumerRunTurns+=100;
}

Producer::Producer(Buffer* pbptr, Consumer* coptr):BufferPtr(pbptr), ConsumerPtr(coptr){}

void Producer::prods(){
    for(int i = 0; i<100; ++i){
        if(ConsumerPtr->isConsumerDead()){
            break;
        }
        BufferPtr->put();
        std::this_thread::sleep_for(std::chrono::milliseconds(60));
    }
}

以前的代码:

#include <iostream>
#include <thread>
#include <mutex>          // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable

using namespace std;

std::mutex mu; //mutex
//std::unique_lock<mutex> locker(mu); //unique_lock
std::condition_variable cond;

std::mutex m_mutex;

class Buffer {
public:
Buffer():count(0) {}
void put();
int get();
bool isBufferEmpty();
bool isBufferFull();
private:
int count;
}; //end class Buffer

//Thread Class and Functions inside:
class Consumer{
public:
    Consumer(Buffer* cbptr);
    void cons();
    bool isConsumerDead();
private:
    Buffer* BufferPtr;
    int consumerDeadFlag;
};

class Producer{
public:
    Producer(Buffer* pbptr, Consumer* coptr);
    void prods();
private:
    Buffer* BufferPtr;
    Consumer* ConsumerPtr;
};

int main(){
    Buffer* tc = new Buffer();
    Consumer* cdtc = new Consumer(tc);
    Producer* pdtc = new Producer(tc,cdtc);
    std::thread t[5]; //declare an array of thread objects
    int i = 0;
    for(; i<3; ++i){
        t[i]=std::thread(&Producer::prods,std::ref(pdtc));
    }
    for(; i<5; ++i){
        t[i]=std::thread(&Consumer::cons,std::ref(cdtc));
    }

    cout << "Debug1" << endl;
    cout << "Debug1" << endl;
    cout << "Debug1" << endl;

    for(i = 0; i<5; ++i){
        t[i].join();
        cout << "now i is :" << i << endl;
    }

    cout << "All threads terminated" << endl;
    return 0;
}

void Buffer::put(){
    std::unique_lock<mutex> locker(mu); //unique_lock
    while(isBufferFull()==1){
        cond.wait(locker);
    }
    if(isBufferFull()==0)
        cond.notify_one();
    ++count;
    std::cout << "producer thread" << this_thread::get_id() << ",count = " << count << endl;
}
int Buffer::get(){
    std::unique_lock<mutex> locker(mu); //unique_lock
    while(isBufferEmpty()==1){
        cond.wait(locker);
    }
    if(isBufferEmpty()==0)
        cond.notify_one();
    --count;
    std::cout << "consumer thread" << this_thread::get_id() << ",count = " << count << endl;
    return count;
}
bool Buffer::isBufferEmpty(){
    if(count <=0){
        std::cout << "buffer is empty, consumer thread" << this_thread::get_id() << " is about to suspend." << endl;
        return 1;
    }
    else
        return 0;
}
bool Buffer::isBufferFull(){
    if(count >=10){
        std::cout << "buffer is full, producer thread" << this_thread::get_id() << " is about to suspend." << endl;
        return 1;
    }
    else
        return 0;
}

Consumer::Consumer(Buffer* cbptr):BufferPtr(cbptr){
    consumerDeadFlag = 0;
}

bool Consumer::isConsumerDead(){
    if(consumerDeadFlag > 1){
        cout << "consumer Dead" << endl;
        cout << "consumer Dead" << endl;
        cout << "consumer Dead" << endl;
        return 1;
    }
    else
        return 0;
}

void Consumer::cons(){
    for(int i = 0; i<100; ++i){
        BufferPtr->get();
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    ++consumerDeadFlag;
}

Producer::Producer(Buffer* pbptr, Consumer* coptr):BufferPtr(pbptr), ConsumerPtr(coptr){}

void Producer::prods(){
    for(int i = 0; i<100; ++i){
        if(ConsumerPtr->isConsumerDead()){
            break;
        }
        BufferPtr->put();
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

输出:

producer threadDebug12,count = 1
producer thread4,count = 2

producer thread3,count = 3
Debug1
Debug1
consumer thread5,count = 2
consumer thread6,count = 1
producer thread2,count = 2
consumer thread5,count = 1
producer thread4,count = 2
producer thread3,count = 3
consumer thread6,count = 2
producer thread2,count = 3
producer thread4,count = 4
producer thread3,count = 5
consumer thread5,count = 4
consumer thread6,count = 3
producer thread2,count = 4
producer thread4,count = 5
consumer thread5,count = 4
producer thread3,count = 5
consumer thread6,count = 4
producer thread2,count = 5
consumer thread5,count = 4
producer thread4,count = 5
producer thread3,count = 6
consumer thread6,count = 5
producer thread2,count = 6
producer thread4,count = 7
producer thread3,count = 8
consumer thread5,count = 7
consumer thread6,count = 6
producer thread2,count = 7
consumer thread5,count = 6
producer thread4,count = 7
producer thread3,count = 8
consumer thread6,count = 7
producer thread2,count = 8
consumer thread5,count = 7
producer thread4,count = 8
producer thread3,count = 9
consumer thread6,count = 8
producer thread2,count = 9
producer thread4,count = 10
consumer thread5,count = 9
producer thread3,count = 10
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread3 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
buffer is full, producer thread4 is about to suspend.
consumer thread5,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread2 is about to suspend.
consumer thread6,count = 9
producer thread4,count = 10
buffer is full, producer thread2 is about to suspend.
consumer thread5,count = 9
producer thread2,count = 10
buffer is full, producer thread3 is about to suspend.
consumer thread6,count = 9
producer thread3,count = 10
buffer is full, producer thread4 is about to suspend.
buffer is full, producer thread2 is about to suspend.

等(由于字数限制)

consumer Dead
consumer Dead
consumer Dead

标签: c++multithreadingc++11concurrency

解决方案


首先,std::thread::join不会杀死线程 - 它等待线程完成。

您有 3 个生产者线程(每个生产 100 个项目),但只有 2 个消费者线程(每个消费 100 个项目)。这意味着有 100 件商品的生产过剩。再加上一个容量只有 10 个项目的缓冲区,您的生产者永远不会在消费者完成之前自行完成

这意味着您需要一种机制来提前结束生产者循环(即,一旦消费者循环结束)。

但是,您解决此问题的尝试使您打开了无限阻塞的线程(类似于死锁,但不完全是)。我突然想到的场景(这并不意味着这是唯一的一个)是一个或多个生产者线程在cond.wait(locker);(in Buffer::put) 上被阻塞,但所有消费者线程都已结束(这意味着cond.notify_one();inBuffer::get将不再发生)。因此,那些生产者线程将永远等待,并且相应的join(s)不会返回。

为了避免这种情况,可以考虑使用std::condition_variable::wait_for代替std::condition_variable::wait来避免阻塞等待。每次发生超时时,您都有机会检查消费者是否已经结束,并在这种情况下结束生产者线程。

检查消费者线程是否已结束的方式也可以改进。一种选择是首先join使用 2 个消费者线程。当两个消费者线程都加入时,向所有生产者线程发出信号,它们也应该结束,然后join是生产者线程。有问题的信号可以是例如。astd::atomic<bool>在生产者的每次迭代中检查。如果将BufferPtr->put();更改为 a BufferPtr->try_put();,这将最有效,它要么在缓冲区中插入一个项目,要么在缓冲区已满时立即返回(或者如果您愿意,可以在超时后返回)。

请注意,您的代码中还有更多问题(尤其是您使用相同的代码condition_variable来表示“非空”和“非满”),但它们不在此问题的范围内。


推荐阅读