我刚学会使用C ++线程库。 如果有人好奇 - 我的代码只是教程https://www.youtube.com/watch?v=13dFggo4t_I&t=6m45s
中的修改版本我写了一个简单的生产者/消费者代码。我试图引入一个睡眠来使生产者和消费者以锁步的方式生产,所以物品一旦生产就会被消耗掉。但让生产者等待导致一些僵局。我无法弄清楚为什么。 你能帮我指出我在代码中缺少什么吗?
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
#include <chrono>
#define BUFFER_SIZE 10
std::mutex mu;
std::condition_variable full,empty;
std::vector<int> vec;
int i=0;
std::chrono::milliseconds slp(10);
void produce()
{
while (i < 2*BUFFER_SIZE) {
std::unique_lock<std::mutex> locker(mu);
full.wait(locker, [] {return vec.size() != BUFFER_SIZE;});
vec.push_back(i++);
empty.notify_one();
//std::this_thread::sleep_for(slp); <--- introducing this causes program to hang.
}
}
void consume()
{
while(!vec.empty()) {
std::unique_lock<std::mutex> locker(mu);
empty.wait(locker, [] {return !vec.empty();});
std::cout << "Consumed:" << vec.back() <<"\n";
vec.pop_back();
full.notify_one();
}
}
int main() {
vec.reserve(BUFFER_SIZE*2);
std::thread producer(produce), consumer(consume);
producer.join();
consumer.join();
return 0;
}
编辑:
void produce()
{
while (i < 2*BUFFER_SIZE) {
std::unique_lock<std::mutex> locker(mu);
full.wait(locker, [] {return vec.size() != BUFFER_SIZE;});
vec.push_back(i++);
locker.unlock();
empty.notify_one();
std::this_thread::sleep_for(slp);
}
}
void consume()
{
while(!vec.empty()) {
std::unique_lock<std::mutex> locker(mu);
empty.wait(locker, [] {return !vec.empty();});
std::cout << "Consumed:" << vec.back() <<"\n";
vec.pop_back();
locker.unlock();
full.notify_one();
}
}
答案 0 :(得分:1)
你有几个问题:
消费者在没有持有互斥锁的情况下访问vec
:
while(!vec.empty()) {
通过确保vec
的所有加入都是&#34;内部&#34;互斥体。
如果消费者超越生产者并设法清空vec
,它将提前退出。您可以通过使用其他一些机制来指明完成情况来解决此问题。
您正在使用互斥锁执行某些处理/ io / sleep,从而减少可能的并发性。理想情况下,您只应在访问共享状态时保留互斥锁。
const unsigned BUFFER_SIZE = 10;
std::mutex mu;
std::condition_variable full,empty;
std::vector<int> vec;
const std::chrono::milliseconds slp(10);
auto done = false;
void produce()
{
for (auto i = 0u; i < 2 * BUFFER_SIZE; ++i) {
std::unique_lock<std::mutex> locker(mu);
full.wait(locker, [] {return vec.size() < BUFFER_SIZE;});
auto was_empty = vec.empty();
vec.push_back(i);
locker.unlock();
// Only notify if the buffer was empty before the push_back
if (was_empty) {
empty.notify_all();
}
std::this_thread::sleep_for(slp);
}
}
void consume()
{
for (;;) {
std::unique_lock<std::mutex> locker(mu);
while (vec.empty()) {
if (done) {
return;
}
empty.wait(locker);
}
auto was_full = vec.size() >= BUFFER_SIZE;
auto value = vec.back();
vec.pop_back();
locker.unlock();
if (was_full) {
full.notify_all();
}
std::cout << "Consumed: " << value << '\n';
}
}
int main() {
vec.reserve(BUFFER_SIZE*2);
std::thread producer(produce), consumer(consume);
producer.join();
// Produce some more
producer = std::thread(produce);
producer.join();
// Produce A LOT more
std::vector<std::thread> many_producers(8);
for (auto&& t : many_producers) {
t = std::thread(produce);
}
for (auto && t : many_producers) {
t.join();
}
// Tell consumer we are done producing
{
std::lock_guard<std::mutex> lock(mu);
done = true;
}
empty.notify_one();
consumer.join();
}