C++多线程打工人

        为啥写这个,今天面试问到了~当时基于信号量写了一个单线程+无锁队列的实现,但是面试官实际想要的是多线程+条件变量实现的方式。

C++多线程打工人_第1张图片

基本概念

        生产者消费者模型是一种常见的并发设计模式,用于处理生产者(生成数据)和消费者(处理数据)之间的协调问题。在多线程环境中,生产者和消费者可能运行在不同的线程中,因此需要同步机制来避免竞态条件和确保数据的一致性。        

C++多线程打工人_第2张图片

        在生产者消费者模型中,生产者负责生成数据并将其放入一个共享的缓冲区,而消费者则从缓冲区中取出数据进行处理。共享缓冲区通常是有限大小的,这意味着生产者在缓冲区满时必须等待,而消费者在缓冲区空时也必须等待。

开搞

C++多线程打工人_第3张图片

        C++中的生产者消费者模型通常依赖于线程库(如C++11标准中的线程库)和同步原语(如互斥锁、条件变量等)来实现。

        为了实现生产者消费者模型,我们需要以下组件:

  • 共享缓冲区:通常是一个队列,用于存储生产者生成的数据。
  • 互斥锁:用于保护共享缓冲区,确保同一时间只有一个线程可以访问缓冲区。
  • 条件变量:用于线程间的同步,允许线程在某个条件不满足时等待,并在条件满足时被唤醒。

以下是一个简单的生产者消费者模型实现示例:

首先是一个生产者消费者队列,提供了Produce和Consume能力,并维护了一个公共队列作为数据存储。

template 
class ProduceConsumeQueue {
public:
	ProduceConsumeQueue(uint32_t size) : sz_(size) {}

	void Produce(const T& data) {
		std::unique_lock ulock(lock_);

		produce_cond_.wait(ulock, [this]() {
			return this->queue_.size() < sz_;
		});

		queue_.push(data);
		std::cout << "produce: " << data << std::endl;

		consume_cond_.notify_one();
	}

	T Consume() {
		std::unique_lock ulock(lock_);

		consume_cond_.wait(ulock, [this]() {
			return !queue_.empty();
		});

		auto data = queue_.front();
		queue_.pop();

		std::cout << "consume: " << data << std::endl;

		produce_cond_.notify_one();
		return data;
	}

private:
	std::mutex lock_;
	std::condition_variable produce_cond_;
	std::condition_variable consume_cond_;

	uint32_t sz_{0};
	std::queue queue_;
};

        基于这个队列提供的生产消费能力,分别实现:

        一个资产阶级C++多线程打工人_第4张图片:        多个打工人C++多线程打工人_第5张图片

class Producer {
public:
	Producer() = default;

	void Init(std::shared_ptr >& queue) {
		queue_ = queue;
	}

	void Produce() {
		int i = 0;
		while (true) {
			sleep(1);
			queue_->Produce(i++);
		}
	}

private:
	std::shared_ptr > queue_;
};

class Consumer {
public:
	Consumer() = default;

	void Init(std::shared_ptr >& queue) {
		queue_ = queue;
	}

	void Consume() {
		while (true) {
			auto val = queue_->Consume();
		}
	}

private:
	std::shared_ptr > queue_;
};

int main() {
	auto pc_queue = std::make_shared >(3);

	std::vector producers(3);  // 打工人
	std::vector consumers(1);  // 资产阶级

	std::vector pthreads;
	std::vector cthreads;

	for (auto& p : producers) {
		p.Init(pc_queue);
		pthreads.emplace_back(std::thread(&Producer::Produce, &p));
	}

	for (auto& c : consumers) {
		c.Init(pc_queue);
		cthreads.emplace_back(std::thread(&Consumer::Consume, &c));
	}

	// join
	pause();

	return 0;
}

 啪一下很快啊C++多线程打工人_第6张图片

code % g++ product_consume.cpp -std=c++17
code % ./a.out                           
produce: 0
consume: 0
produce: 0
consume: 0
produce: 0
consume: 0
produce: 1
consume: 1
produce: 1
consume: 1
produce: 1
consume: 1
^C
code % 

        上面的生产者消费者模型是一种简单而有效的并发编程模型,它具有以下优点:易于理解和实现、可以很好地解决多个线程之间的数据共享问题、可以提高程序的性能。但是,也存在一些缺点:可能存在饥饿问题(可以对生产者、消费者排队处理)。

        生产者消费者模型可以应用于各种场景,例如:多线程文件读写、多线程网络通信、多线程数据库访问等。

        上面调用std::condition_variable的wait操作的第二个参数是一个判断条件,收到条件信号的时候会判断是再判断是否满足条件,不满足会循环再等,省得我们手动再写一个循环判断了。
        在设计生产者消费者模型时,还需要考虑如何优雅地终止线程和处理异常情况,如join。

        附完整代码:https://github.com/Fireplusplus/Code_Cpp/blob/master/ProducerConsumer.cpp

你可能感兴趣的:(C++,教程,c++,生产者消费者,条件变量,生产者,消费者)