Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 一个生产者线程,几个消费者_C++_Multithreading_C++11 - Fatal编程技术网

C++ 一个生产者线程,几个消费者

C++ 一个生产者线程,几个消费者,c++,multithreading,c++11,C++,Multithreading,C++11,我有一个生产者线程和几个消费者,每个消费者都有:拥有数据和唯一id的自己的队列。 我使用std::map来标识线程的每个队列 typedef std::map<int, std::queue<Task>> TaskMap; TaskMap inputQueue; TaskMap outputQueue; typedef std::map TaskMap; 任务映射输入队列; 任务映射输出队列; 每个使用者线程处理其队列中的数据,若队列为空,则线程必须等待数据。 如果我

我有一个生产者线程和几个消费者,每个消费者都有:拥有数据和唯一id的自己的队列。 我使用std::map来标识线程的每个队列

typedef std::map<int, std::queue<Task>> TaskMap;
TaskMap inputQueue;
TaskMap outputQueue;
typedef std::map TaskMap;
任务映射输入队列;
任务映射输出队列;
每个使用者线程处理其队列中的数据,若队列为空,则线程必须等待数据。 如果我只想使用一个线程,我可以使用std::condition_变量和std::unique_锁,但我有几个使用者,所以我需要几个std::condition_变量,但我无法将它们保存在容器中(复制/分配被删除)。 所以我使用这样的代码

while(q.empty()) {
    std::cout << "waiting...\n";
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
while(q.empty()){

std::coutOne
std::conditional_变量
和One
std::mutex
应该足够了

Task t;
{
  std::unique_lock<std::mutex> lock(mtx);
  while (q.empty())
    cond_var.wait(lock);
  t = std::move(q.front());
  q.pop_front();
}
任务t;
{
std::唯一锁(mtx);
while(q.empty())
条件变量等待(锁定);
t=std::move(q.front());
q、 pop_front();
}
和主线程将做

{
  std::lock_guard<std::mutex> lock(mtx);
  q.emplace_front(/*...*/);
  cond_var.notify_all();
}
{
标准:锁和防护锁(mtx);
q、 炮位前方(/*…*/);
cond_var.notify_all();
}

主线程将唤醒所有线程,但大多数线程将返回睡眠状态,因为它们的队列仍然是空的。

因为每个使用者都有自己的队列,并且所有使用者只有一个生产者,所以本质上是一个生产者一个消费者的场景


换句话说,您没有在所有使用者之间共享单个队列。

调用OOP以执行此任务:

class ConcurentTaskQueue{
    std::mutex lock;
    std::condition_variable m_ConditionVariable;
    std::queue<Task> m_TaskQueue;


public:
    Task getTask(){
        std::unique_lock<std::mutex> synchLock (lock); //NOTE: consider doing this with a while(programIsRunning){} loop
        while(m_TaskQueue.empty()){
            m_ConditionVariable.wait(synchLock);
        } 
        Task task(std::move(m_TaskQueue.front()));
        m_TaskQueue.pop();
        return task;
    }

    void addTask (Task task){
        std::unique_lock<std::mutex> synchLock (lock);
        m_TaskQueue.push(std::move(task));
        m_ConditionVariable.notify_one();
    }
};
类ConcurentTaskQueue{
std::互斥锁;
std::条件变量m\U条件变量;
std::队列m_TaskQueue;
公众:
任务getTask(){
STD::UnQueQueLoad同步锁(锁);/Note:考虑用一段时间(程序运行){}循环来做这件事
while(m_TaskQueue.empty()){
m_ConditionVariable.wait(synchLock);
} 
任务(std::move(m_TaskQueue.front());
m_TaskQueue.pop();
返回任务;
}
void addTask(任务任务){
std::唯一锁定同步锁定(锁定);
m_TaskQueue.push(std::move(task));
m_ConditionVariable.notify_one();
}
};
现在简单地说:

std::map<size_t,ConcurentTaskQueue> inputQueue;
std::thread producer ([&]{
      Task task = produceTask()
      inputQueue[ID].addTask(task);
});

std::thread consumer1([&]{
      Task task = inputQueue[ID].getTask();
});


std::thread consumer2([&]{
      Task task = inputQueue[ID].getTask();
});
std::映射输入队列;
线程生成器([&]{
任务任务=生产任务()
inputQueue[ID].addTask(任务);
});
std::线程使用者1([&]{
Task Task=inputQueue[ID]。getTask();
});
std::线程使用者2([&]{
Task Task=inputQueue[ID]。getTask();
});
编辑2:
使用线程池

如果您这样做不是为了研究/学习,而是为了生产代码,我建议您使用众多实现中的一种。正确、高效和可扩展地实现这一点并不容易,但其他人已经解决了问题。有许多选项,例如:


缺少副本/分配并不意味着你不能将东西放入容器。只有部分操作将不可用。你仍然可以使用
emplace
at
。请查看。“但是我有几个消费者,所以我需要几个std::condition\u变量”不正确。那么我如何识别我需要唤醒的线程呢?@user3365834您在这里非常需要一个线程池。不要试图重新发明轮子。但是性能呢?唤醒多个线程是一个沉重的操作,不是吗?所以使用
notify_one()
并且只唤醒一个使用者,该使用者将接受任务并处理它。您不能将
std::mutex
传递给
条件变量::wait()
,并且此示例不是异常安全的。您不应该使用
mtx.lock()
mtx.unlock())
,改用
unique\u lock
。@JonathanWakely谢谢你,没有注意到。不,你让事情变得更糟了。不要在线程之间共享
unique\u lock
,不要调用
lock()
unlock()
明确地说。我已经为您修复了它。生产者为所有消费者生成数据。在生产者线程中,我有数据和消费者id,所以我将这些数据放在id标识的队列中。@user3365834仍然,只要消费者不使用同一队列从中获取数据,它基本上是单个生产者单个消费者。事实上,没有“不止一个消费者,而且您的生产者为多个消费者扮演单一生产者,这并不能改变线程以单一生产者-单一消费者的方式彼此同步的事实。@Maxim Egorushkin据我所知,一个线程填满队列,多个线程可以使用您的实现的一个队列。”tion不考虑在
等待
时出现虚假唤醒,这可能会导致未定义的行为。你是对的,但这只是方向,不是一个完整的实现。那么请在回答中包含你的假设。错误的代码比没有代码更糟糕。你不通过添加原子来解决虚假唤醒,你解决的是虚假唤醒在这种情况下,它应该是
while(m_TaskQueue.empty())
而不是
if(m_TaskQueue.empty())
返回std::move(task);
是错误的,它阻止了RVO,
返回任务;
仍然可以使用移动构造函数,但也允许RVO。考虑到在这里的答案中建议的糟糕的坏代码,我倾向于同意。