C++;生产者-消费者,同一消费者线程捕获所有任务 我在C++中实现了生产者消费者项目,当我运行这个程序时,同一个消费者几乎抢占了所有的工作,而不让任何其他的消费线程抢占任何一个。有时,其他线程确实会得到一些工作,但随后该线程会控制一段时间。例如,TID 10可以抓取几乎所有的工作,但是TID 12会突然抓取它,而没有其他消费者线程在两者之间获得工作
知道为什么其他线程没有机会获得工作吗C++;生产者-消费者,同一消费者线程捕获所有任务 我在C++中实现了生产者消费者项目,当我运行这个程序时,同一个消费者几乎抢占了所有的工作,而不让任何其他的消费线程抢占任何一个。有时,其他线程确实会得到一些工作,但随后该线程会控制一段时间。例如,TID 10可以抓取几乎所有的工作,但是TID 12会突然抓取它,而没有其他消费者线程在两者之间获得工作,c++,multithreading,producer-consumer,C++,Multithreading,Producer Consumer,知道为什么其他线程没有机会获得工作吗 #include <thread> #include <iostream> #include <mutex> #include <condition_variable> #include <deque> #include <csignal> #include <unistd.h> using namespace std; int max_queue_size = 100;
#include <thread>
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <deque>
#include <csignal>
#include <unistd.h>
using namespace std;
int max_queue_size = 100;
int num_producers = 5;
int num_consumers = 7;
int num_operations = 40;
int operations_created = 0;
thread_local int operations_created_by_this_thread = 0;
int operations_consumed = 0;
thread_local int operations_consumed_by_this_thread = 0;
struct thread_stuff {
int a;
int b;
int operand_num;
char operand;
};
char operands[] = {'+', '-', '/', '*'};
deque<thread_stuff> q;
bool finished = false;
condition_variable cv;
mutex queue_mutex;
void producer(int n) {
while (operations_created_by_this_thread < num_operations) {
int oper_num = rand() % 4;
thread_stuff equation;
equation.a = rand();
equation.b = rand();
equation.operand_num = oper_num;
equation.operand = operands[oper_num];
while ((operations_created - operations_consumed) >= max_queue_size) {
// don't do anything until it has space available
}
{
lock_guard<mutex> lk(queue_mutex);
q.push_back(equation);
operations_created++;
}
cv.notify_all();
operations_created_by_this_thread++;
this_thread::__sleep_for(chrono::seconds(rand() % 2), chrono::nanoseconds(0));
}
{
lock_guard<mutex> lk(queue_mutex);
if(operations_created == num_operations * num_producers){
finished = true;
}
}
cv.notify_all();
}
void consumer() {
while (true) {
unique_lock<mutex> lk(queue_mutex);
cv.wait(lk, [] { return finished || !q.empty(); });
if(!q.empty()) {
thread_stuff data = q.front();
q.pop_front();
operations_consumed++;
operations_consumed_by_this_thread++;
int ans = 0;
switch (data.operand_num) {
case 0:
ans = data.a + data.b;
break;
case 1:
ans = data.a - data.b;
break;
case 2:
ans = data.a / data.b;
break;
case 3:
ans = data.a * data.b;
break;
}
cout << "Operation " << operations_consumed << " processed by PID " << getpid()
<< " TID " << this_thread::get_id() << ": "
<< data.a << " " << data.operand << " " << data.b << " = " << ans << " queue size: "
<< (operations_created - operations_consumed) << endl;
}
this_thread::yield();
if (finished) break;
}
}
void usr1_handler(int signal) {
cout << "Status: Produced " << operations_created << " operations and "
<< (operations_created - operations_consumed) << " operations are in the queue" << endl;
}
void usr2_handler(int signal) {
cout << "Status: Consumed " << operations_consumed << " operations and "
<< (operations_created - operations_consumed) << " operations are in the queue" << endl;
}
int main(int argc, char *argv[]) {
if (argc < 5) {
cout << "Invalid number of parameters passed in" << endl;
exit(1);
}
max_queue_size = atoi(argv[1]);
num_operations = atoi(argv[2]);
num_producers = atoi(argv[3]);
num_consumers = atoi(argv[4]);
// signal(SIGUSR1, usr1_handler);
// signal(SIGUSR2, usr2_handler);
thread producers[num_producers];
thread consumers[num_consumers];
for (int i = 0; i < num_producers; i++) {
producers[i] = thread(producer, num_operations);
}
for (int i = 0; i < num_consumers; i++) {
consumers[i] = thread(consumer);
}
for (int i = 0; i < num_producers; i++) {
producers[i].join();
}
for (int i = 0; i < num_consumers; i++) {
consumers[i].join();
}
cout << "finished!" << endl;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
int max_queue_size=100;
int num_生产者=5;
int num_消费者=7;
int num_运算=40;
创建的整数运算=0;
线程\u本地整数操作\u由\u创建\u此\u线程=0;
消耗的整数运算=0;
线程\u本地整数操作\u被\u此\u线程消耗\u=0;
结构线程{
INTA;
int b;
整数操作数;
字符操作数;
};
字符操作数[]={'+','-','/','*'};
德克q;
bool finished=false;
条件变量cv;
互斥队列\互斥;
无效生成程序(int n){
while(操作\u由\u创建\u此\u线程=最大队列大小){
//在它有可用空间之前不要做任何事情
}
{
锁定保护lk(队列互斥);
q、 推回(方程式);
操作创建++;
}
cv.通知所有人();
由线程++创建的操作;
此线程:::(时钟::秒(rand()%2),时钟::纳秒(0));
}
{
锁定保护lk(队列互斥);
if(创建的操作数==num\u操作数*num\u生产者){
完成=正确;
}
}
cv.通知所有人();
}
无效消费者(){
while(true){
唯一锁lk(队列互斥);
cv.wait(lk,[{return finished | | |!q.empty();});
如果(!q.empty()){
thread_stuff data=q.front();
q、 pop_front();
操作(u++);
这个线程所消耗的操作数++;
int ans=0;
开关(数据操作数){
案例0:
ans=数据a+数据b;
打破
案例1:
ans=data.a-data.b;
打破
案例2:
ans=数据a/data.b;
打破
案例3:
ans=数据a*数据b;
打破
}
cout您一直都在持有互斥锁,包括持有互斥锁时的yield()
-ing
像在生产者代码中一样,定义唯一的_锁,从队列中弹出并原子地递增计数器
我看到您有一个最大队列大小。如果队列已满,您需要第二个条件让生产者等待,并且消费者在消费项目时会发出此条件的信号。您一直都在持有互斥锁,包括持有互斥锁时的yield()
-ing
像在生产者代码中一样,定义唯一的_锁,从队列中弹出并原子地递增计数器
我看到您有一个最大队列大小。如果队列已满,您需要第二个条件让生产者等待,消费者在消费项目时将发出此条件的信号
知道为什么其他线程没有机会获得工作吗
#include <thread>
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <deque>
#include <csignal>
#include <unistd.h>
using namespace std;
int max_queue_size = 100;
int num_producers = 5;
int num_consumers = 7;
int num_operations = 40;
int operations_created = 0;
thread_local int operations_created_by_this_thread = 0;
int operations_consumed = 0;
thread_local int operations_consumed_by_this_thread = 0;
struct thread_stuff {
int a;
int b;
int operand_num;
char operand;
};
char operands[] = {'+', '-', '/', '*'};
deque<thread_stuff> q;
bool finished = false;
condition_variable cv;
mutex queue_mutex;
void producer(int n) {
while (operations_created_by_this_thread < num_operations) {
int oper_num = rand() % 4;
thread_stuff equation;
equation.a = rand();
equation.b = rand();
equation.operand_num = oper_num;
equation.operand = operands[oper_num];
while ((operations_created - operations_consumed) >= max_queue_size) {
// don't do anything until it has space available
}
{
lock_guard<mutex> lk(queue_mutex);
q.push_back(equation);
operations_created++;
}
cv.notify_all();
operations_created_by_this_thread++;
this_thread::__sleep_for(chrono::seconds(rand() % 2), chrono::nanoseconds(0));
}
{
lock_guard<mutex> lk(queue_mutex);
if(operations_created == num_operations * num_producers){
finished = true;
}
}
cv.notify_all();
}
void consumer() {
while (true) {
unique_lock<mutex> lk(queue_mutex);
cv.wait(lk, [] { return finished || !q.empty(); });
if(!q.empty()) {
thread_stuff data = q.front();
q.pop_front();
operations_consumed++;
operations_consumed_by_this_thread++;
int ans = 0;
switch (data.operand_num) {
case 0:
ans = data.a + data.b;
break;
case 1:
ans = data.a - data.b;
break;
case 2:
ans = data.a / data.b;
break;
case 3:
ans = data.a * data.b;
break;
}
cout << "Operation " << operations_consumed << " processed by PID " << getpid()
<< " TID " << this_thread::get_id() << ": "
<< data.a << " " << data.operand << " " << data.b << " = " << ans << " queue size: "
<< (operations_created - operations_consumed) << endl;
}
this_thread::yield();
if (finished) break;
}
}
void usr1_handler(int signal) {
cout << "Status: Produced " << operations_created << " operations and "
<< (operations_created - operations_consumed) << " operations are in the queue" << endl;
}
void usr2_handler(int signal) {
cout << "Status: Consumed " << operations_consumed << " operations and "
<< (operations_created - operations_consumed) << " operations are in the queue" << endl;
}
int main(int argc, char *argv[]) {
if (argc < 5) {
cout << "Invalid number of parameters passed in" << endl;
exit(1);
}
max_queue_size = atoi(argv[1]);
num_operations = atoi(argv[2]);
num_producers = atoi(argv[3]);
num_consumers = atoi(argv[4]);
// signal(SIGUSR1, usr1_handler);
// signal(SIGUSR2, usr2_handler);
thread producers[num_producers];
thread consumers[num_consumers];
for (int i = 0; i < num_producers; i++) {
producers[i] = thread(producer, num_operations);
}
for (int i = 0; i < num_consumers; i++) {
consumers[i] = thread(consumer);
}
for (int i = 0; i < num_producers; i++) {
producers[i].join();
}
for (int i = 0; i < num_consumers; i++) {
consumers[i].join();
}
cout << "finished!" << endl;
}
这项民意调查令人不安:
while ((operations_created - operations_consumed) >= max_queue_size)
{
// don't do anything until it has space available
}
你可以在循环中尝试最小的延迟…这是一个“坏邻居”,并且可以“消耗”一个核心
知道为什么其他线程没有机会获得工作吗
#include <thread>
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <deque>
#include <csignal>
#include <unistd.h>
using namespace std;
int max_queue_size = 100;
int num_producers = 5;
int num_consumers = 7;
int num_operations = 40;
int operations_created = 0;
thread_local int operations_created_by_this_thread = 0;
int operations_consumed = 0;
thread_local int operations_consumed_by_this_thread = 0;
struct thread_stuff {
int a;
int b;
int operand_num;
char operand;
};
char operands[] = {'+', '-', '/', '*'};
deque<thread_stuff> q;
bool finished = false;
condition_variable cv;
mutex queue_mutex;
void producer(int n) {
while (operations_created_by_this_thread < num_operations) {
int oper_num = rand() % 4;
thread_stuff equation;
equation.a = rand();
equation.b = rand();
equation.operand_num = oper_num;
equation.operand = operands[oper_num];
while ((operations_created - operations_consumed) >= max_queue_size) {
// don't do anything until it has space available
}
{
lock_guard<mutex> lk(queue_mutex);
q.push_back(equation);
operations_created++;
}
cv.notify_all();
operations_created_by_this_thread++;
this_thread::__sleep_for(chrono::seconds(rand() % 2), chrono::nanoseconds(0));
}
{
lock_guard<mutex> lk(queue_mutex);
if(operations_created == num_operations * num_producers){
finished = true;
}
}
cv.notify_all();
}
void consumer() {
while (true) {
unique_lock<mutex> lk(queue_mutex);
cv.wait(lk, [] { return finished || !q.empty(); });
if(!q.empty()) {
thread_stuff data = q.front();
q.pop_front();
operations_consumed++;
operations_consumed_by_this_thread++;
int ans = 0;
switch (data.operand_num) {
case 0:
ans = data.a + data.b;
break;
case 1:
ans = data.a - data.b;
break;
case 2:
ans = data.a / data.b;
break;
case 3:
ans = data.a * data.b;
break;
}
cout << "Operation " << operations_consumed << " processed by PID " << getpid()
<< " TID " << this_thread::get_id() << ": "
<< data.a << " " << data.operand << " " << data.b << " = " << ans << " queue size: "
<< (operations_created - operations_consumed) << endl;
}
this_thread::yield();
if (finished) break;
}
}
void usr1_handler(int signal) {
cout << "Status: Produced " << operations_created << " operations and "
<< (operations_created - operations_consumed) << " operations are in the queue" << endl;
}
void usr2_handler(int signal) {
cout << "Status: Consumed " << operations_consumed << " operations and "
<< (operations_created - operations_consumed) << " operations are in the queue" << endl;
}
int main(int argc, char *argv[]) {
if (argc < 5) {
cout << "Invalid number of parameters passed in" << endl;
exit(1);
}
max_queue_size = atoi(argv[1]);
num_operations = atoi(argv[2]);
num_producers = atoi(argv[3]);
num_consumers = atoi(argv[4]);
// signal(SIGUSR1, usr1_handler);
// signal(SIGUSR2, usr2_handler);
thread producers[num_producers];
thread consumers[num_consumers];
for (int i = 0; i < num_producers; i++) {
producers[i] = thread(producer, num_operations);
}
for (int i = 0; i < num_consumers; i++) {
consumers[i] = thread(consumer);
}
for (int i = 0; i < num_producers; i++) {
producers[i].join();
}
for (int i = 0; i < num_consumers; i++) {
consumers[i].join();
}
cout << "finished!" << endl;
}
这项民意调查令人不安:
while ((operations_created - operations_consumed) >= max_queue_size)
{
// don't do anything until it has space available
}
您可以在循环中尝试最小的延迟…这是一个“坏邻居”,可以“消耗”一个内核。您的代码几乎没有问题:
使用正常变量进行线程间通信
以下是一个例子:
int operations_created = 0;
int operations_consumed = 0;
void producer(int n) {
[...]
while ((operations_created - operations_consumed) >= max_queue_size) { }
后来
void consumer() {
[...]
operations_consumed++;
这只适用于没有优化的x86体系结构,即-O0
。一旦我们尝试启用优化,编译器将优化while循环以:
void producer(int n) {
[...]
if ((operations_created - operations_consumed) >= max_queue_size) {
while (true) { }
}
所以,你的程序就挂在这里,你可以
为什么会发生这种情况?从单线程程序的角度来看,while(condition){operators}
完全等同于if(condition)while(true){operators}
ifoperators
不改变条件
为了解决这个问题,我们应该使用std::atomic
而不是simpleint
。这些都是为线程间通信而设计的,因此编译器将避免此类优化并生成正确的程序集
消费者在yield()时锁定互斥锁
请看一下这个片段:
void consumer() {
while (true) {
unique_lock<mutex> lk(queue_mutex);
[...]
this_thread::yield();
[...]
}
这仍然不能保证只有一个线程可以完成大部分任务。当我们在producer中执行notify_all()
时,所有线程都会被唤醒,但只有一个线程会锁定互斥锁。由于我们安排的工作很小,当producer调用notify_all()
时,我们的线程将完成工作,完成yield()
并将为下一步工作做好准备
那么为什么这个线程会锁定互斥锁,而不是另一个呢?我猜这是由于CPU缓存和忙等待造成的。刚刚完成工作的线程是“热的”,它在CPU缓存中,准备锁定互斥锁。在进入睡眠之前,它也可能尝试忙着等待互斥锁几个周期,这增加了它赢得更多的机会
要解决这个问题,我们可以在producer中删除sleep(这样它会更频繁地唤醒其他线程,这样其他线程也会“热”),或者在consum中执行sleep()