c++ - 是“僵局”吗?如何在多线程中避免它?
问题描述
我目前正在学习 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
解决方案
首先,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
来表示“非空”和“非满”),但它们不在此问题的范围内。
推荐阅读
- javascript - 如何将某些拉丁字符转换为西里尔字符
- python - Python Tkinter:Frames 并没有在彼此之下
- python - y 轴刻度固定到 matplotlib 在某些数据点处的第二个 y 轴刻度
- react-native - 为什么地图/谷歌地图不显示门牌号?
- python - Display and Video 360 为暂停广告客户方法引发错误
- flutter - Flutter RenderBox 没有布局
- r - 检索特定 R 包的所有版本
- wordpress - wordpress中文件路径自定义主题中缺少斜杠
- api - 从 laravel8 中的不同服务器以 POST 方法从 API 获取响应
- r - 如何将字符串分配给 Sublime Text 4 中的键绑定?