通过互斥锁和条件变量实现队列的无sleep生产与消费

昨天转了一篇文章将条件变量与互斥锁的结合使用,今天给大家抠出曾经学到的一段高效的处理消息的demo,通过互斥锁实现安全队列,通过条件变量实现队列的工作与休息。

目录结构:

简单介绍:

SignalThread是封装好的线程类,我们所做的核心工作都是在这里完成,其他类也是服务于这里;

MessageList是封装好的安全线程类,基于c++ list实现的;

MessageHanderImp是处理消息的方法,通过继承MessageHander的接口,实现了方法OnMessage,其实只是printf了一下;

message是消息的类,封装了一下我们的消息;

源码解析:

主函数很简单,显而易见SignalThread是我们实现的一个最主要的类,在这里面封装了线程完成了核心功能,主函数只要将它启动即可,之后往封装好的类里面“push”数据,当然这个“push”也是我们封装好的方法postMessage。MessageHanderImp是处理消息的类,message是消息的类。

int main(int argc, char** argv) {

    cout << "----start\n";
    SignalThread signalThread;
    signalThread.start();   //核心线程启动
    
    cout << "----post\n";
    signalThread.postMessage(new MessageHanderImp(), new Message());//往线程里面push消息
    cout << "----post end\n";
    
    getchar();
    return 0;
}

核心代码:

下面是我们实现的核心类SignalThread,注释写的蛮清楚了,类的方法在头文件中直接实现了,其中最重要的是MessageData* hander = m_queue.wait();这边的wait在messageList里面实现。

struct MessageData {
    MessageHander* pHander;
    Message*pMessage;
};

class SignalThread {
public:
    SignalThread() {
        pthread_cond_init(&cond, NULL);
        pthread_mutex_init(&mutex, NULL);//初始化锁和条件变量
    }
    virtual ~SignalThread(){
        pthread_mutex_destroy(&mutex);
	    pthread_cond_destroy(&cond);
    }
    
    void start() {
        pthread_t t;
        
        pthread_mutex_lock(&mutex);
        pthread_create(&t, NULL, run, this);
        
        pthread_cond_wait(&cond, &mutex);//这边等待线程开始时会停止等待
        pthread_mutex_unlock(&mutex);
    }
    void postMessage(MessageHander* hander, Message* message = NULL) { 
        
        MessageData * pessageData = new MessageData();
        pessageData->pHander = hander;
        pessageData->pMessage = message;
        m_queue.push_back(pessageData);//实现往队列推送消息
    }
    
private:
    void* _start(void * arg) {

        while(1){
            
            pthread_mutex_lock(&mutex);
            pthread_cond_signal(&cond);//这里是解开主线程的wait
            pthread_mutex_unlock(&mutex);
            
            //要改成阻塞式的队列
            MessageData* hander = m_queue.wait();//如果没有消息需要进行wait,释放cpu
            if(hander){    //一旦有消息及时处理
                printf("_start SignalThread OnMessage\n");
                hander->pHander->OnMessage(hander->pMessage);//这是一个处理消息的方法
            }
        }
        return NULL;
    }
        
    static void *run(void* pths) {
        SignalThread* THIS = (SignalThread*)pths;
        return THIS->_start(pths);
    }
        
private:
    
    MessageList<MessageData*> m_queue;//这里封装了安全队列,下面讲解
    
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    
};

messageList实现:

这里边实现了模板,最主要就是说一下wait这个方法pthread_cond_wait(&cond, &mutex);实现了阻塞,当队列是空的时候不进行轮询浪费cpu而是wait,至于为什么不用 if (m_msglist.empty()) 而是用 while (m_msglist.empty()) 是因为if (在某些情况下可能会出问题,特别是有多个线程在wait的时候)这里推荐看一下这篇篇文章关于互斥锁和条件变量的:            浅谈互斥锁为什么还要和条件变量配合使用_GT19930910的博客-CSDN博客

还有一个知识点为什么要先执行pthread_cond_signal(&cond);再解锁,这里我就不多解释了,也有一片文章给我们做了解释:

唤醒线程是否要持有锁(Signal With Mutex Locked Or Not?)_D_Guco的博客-CSDN博客

messageList部分代码:

value_type wait()
{
    lock();
    while (m_msglist.empty())
    {

//       LOGE("wait ~~~~~~~~~~~~~~~1");
         pthread_cond_wait(&cond, &mutex);//直到有信号告知有数据来,才结束等待
    }
//  LOGE("wait ~~~~~~~~~~~~~~~2");
    value_type tmp = m_msglist.front();
    m_msglist.pop_front();
    unlock();
    return tmp;
}       

 bool push_back(value_type msg_data)
 {
     lock();
     m_msglist.push_back(msg_data);
     pthread_cond_signal(&cond);//推送完数据之后通知处理数据
     unlock();
     return true;
 }

github完整代码以及编译方法:

​​​git地址:​​​​ mytest-projects: 收集一些c/c++的技术点

cd thread-and-message

mkdir build

cd build

cmake ..

make

./threadmessage

编译截图:

以下是生成环形队列实现生产者和消费者的同步与互斥问题的代码: ``` #include <iostream> #include <queue> #include <thread> #include <chrono> #include <mutex> #include <condition_variable> using namespace std; const int buffer_size = 10; queue<int> buffer; int product_count = 0, consume_count = 0; mutex buffer_mutex; condition_variable buffer_full_cond; condition_variable buffer_empty_cond; void producer_func() { while (true) { unique_lock<mutex> lock(buffer_mutex); buffer_full_cond.wait(lock, []{ return buffer.size() < buffer_size; }); buffer.push(product_count++); cout << "Producer produces: " << product_count - 1 << endl; lock.unlock(); buffer_empty_cond.notify_one(); this_thread::sleep_for(chrono::milliseconds(500)); // 生产者速度慢一些 } } void consumer_func() { while (true) { unique_lock<mutex> lock(buffer_mutex); buffer_empty_cond.wait(lock, []{ return !buffer.empty(); }); int val = buffer.front(); buffer.pop(); cout << "Consumer consumes: " << val << endl; lock.unlock(); buffer_full_cond.notify_one(); this_thread::sleep_for(chrono::milliseconds(200)); // 消费者速度快一些 } } int main() { thread producer_thread(producer_func); thread consumer_thread(consumer_func); producer_thread.join(); consumer_thread.join(); return 0; } ``` 本段代码实现了生成环形队列实现生产者和消费者的同步与互斥问题。在运行时,生产者线程会生产一个产品,并通过条件变量 `buffer_full_cond` 等待,直到队列未满时再将产品添加到队列中;消费者线程会从队列中取走一个产品,并通过条件变量 `buffer_empty_cond` 等待,直到队列非空时再进行消费。同时,使用互斥锁 `buffer_mutex` 来保证队列的数据访问时的线程安全。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值