生产者消费者模型并不属于面向对象的23种设计模式之一,实际上是结构化编程思想中的一种经典模式,使用非常广泛。而在面向对象编程过程中也不会只是面向对象,通常是与结构化编程相辅相成的。好了闲话不多说,下面的例子非常简单的使用了std::thread来实现一个生产者消费者模型。
#include <iostream>
#include <thread>
#include <chrono>
#include <queue>
#include <mutex>
class worker{
public:
worker()=delete;//禁用默认构造
worker& operator=(const worker&)=delete;//禁用赋值
worker& operator=(worker&&)=delete;//禁用右值赋值
explicit worker(bool _v):m_stop_generate(_v),m_stop_consume(_v){
std::cout<<"worker constructor called, thread id: "<<std::this_thread::get_id()<<std::endl;
std::cout<<"对象地址: "<<this<<std::endl;
}
worker(const worker& wkr):m_stop_generate(wkr.m_stop_generate),m_stop_consume(wkr.m_stop_consume){
std::cout<<"worker copy-constructor called, thread id: "<<std::this_thread::get_id()<<std::endl;
std::cout<<"对象地址: "<<this<<std::endl;
}
worker(worker&& wkr){
/*if wkr has other non-static member variables, init or clear them here*/
m_stop_generate = wkr.m_stop_generate;
m_stop_consume = wkr.m_stop_consume;
wkr.m_stop_generate = false;
wkr.m_stop_consume = false;
std::cout<<"worker move-constructor called, thread id: "<<std::this_thread::get_id()<<std::endl;
std::cout<<"对象地址: "<<this<<std::endl;
}
void stop_working(){
//std::lock_guard<std::mutex> lock(mu);
m_stop_generate = true;
m_stop_consume = true;
std::cout<<"stop called."<<std::endl;
}
void generate_data()const{
while(!m_stop_generate){
int v = 1;
{
std::lock_guard<std::mutex> lock(mu);
msgQueue.push(v);
}
std::cout<<"生产者存值, value is: "<<v<<std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50));//50 ms
}
std::cout<<"生产者线程退出。 "<<std::endl;
}
void consume_data()const{
while(!m_stop_consume){
int v;
bool ok;
{
std::lock_guard<std::mutex> lock(mu);
ok = msgQueue.empty();
if(!ok){
v = msgQueue.front();
msgQueue.pop();
}
}
if(!ok)
std::cout<<"消费者取值 ,value is: "<<v<<std::endl;
else
std::cout<<"消费者等待..."<<std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(50));//50 ms
}
std::cout<<"消费者线程退出。 "<<std::endl;
}
~worker(){
std::cout<<"worker de-constructor called, thread id: "<<std::this_thread::get_id()<<std::endl;
std::cout<<"对象地址: "<<this<<std::endl;
}
public:
int m_test_value;
private:
static std::queue<int> msgQueue;//线程共享资源
static std::mutex mu;//线程共享资源
bool m_stop_generate;
bool m_stop_consume;
};
std::queue<int> worker::msgQueue;
std::mutex worker::mu;
void dowork_nonconst(worker &a){
std::cout<<"nonconst adress is: "<<&a<<", value is: "<<a.m_test_value<<std::endl;
}
void dowork_const(const worker &a){
std::cout<<"const adress is: "<<&a<<", value is: "<<a.m_test_value<<std::endl;
}
int main(int argc, char *argv[])
{
worker A(false);
//thread的构造函数会为所有的参数制作一个匿名副本,放入其私有栈底,
//即便可调用对象接收参数用的是引用,它也会先制作副本,再用副本去初始化可调用对象的形参,
//虽然副本在主线程创建,但是所有权归子线程,子线程结束时会自动出栈,回收内存。
//这样做主要是担心传入线程的局部变量,在子线程未返回前就生命周期终止,引发未定义行为。缺点是主线程失去对象控制权
//而这些只是做了一定的防范措施,但是程序员依然可以绕过这些防范机制,比如传指针
//或者强制指定传引用(用std::ref),这些情况下要格外关注变量的生命周期。
//thread th(dowork_nonconst,std::ref(A));
//thread th(dowork_const,worker(10));
//thread th(dowork_const,A);
//成员函数作为线程入口
//thread th(&worker::generate_data,A); //相对安全,因为会调用A的拷贝构造函数,缺点是主线程失去对象的控制权。
std::thread th(&worker::generate_data,&A); // 不安全,A可能在线程返回前就消亡了,优点是主线程可以借此控制子线程行为。
std::thread th2(&worker::consume_data,&A);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
A.stop_working();
th.join();
th2.join();
return 0;
}