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}
if
operators
不改变
条件

为了解决这个问题,我们应该使用
std::atomic
而不是simple
int
。这些都是为线程间通信而设计的,因此编译器将避免此类优化并生成正确的程序集

消费者在
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()