是"deadlock"吗?如何在多线程中避免它?

Is it a "deadlock"? How to avoid it in multithreading?

本文关键字:多线程 deadlock      更新时间:2023-10-16

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

我的环境:

系统: 视窗 10

编译器:mingw-w64x86_64-8.1.0-posix-she-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

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

您有 3 个生产者线程(每个线程生成 100 个项目(,但只有 2 个使用者线程(每个线程使用 100 个项目(。这意味着有 100 件物品正在生产盈余。再加上一个容量只有 10 个项目的缓冲区,在消费者完成之前,您的生产者将永远不会自己完成

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

然而,您尝试解决此问题的尝试使您面临无限阻塞的线程(类似于死锁,但不完全是(。我突然想到的情况(这并不意味着这是唯一的一个(,是当一个或多个生产者线程在cond.wait(locker);上被阻塞时(在Buffer::put年(,但所有使用者线程都已结束(意味着Buffer::get中的cond.notify_one();将不再发生(。因此,这些生产者线程将永远等待,相应的join不会返回。

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

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

请注意,您的代码中还有更多问题(其中最重要的是您使用相同的condition_variable来表示"不为空"和"未满"(,但它们不在此问题的讨论范围内。