• 基于阻塞队列、线程池的生产者消费者模型


    一、线程池

    二、模型实现

    1、准备工作

    2、创建线程

    3、PushTask函数

    4、PopTask函数

    5、线程执行函数Routines

    三、模型测试


    一、线程池

    和内存池一样,线程池也是为了提升效率。一个服务器可能短时间会接收到很多请求,等接收到请求再创建线程,此时就需要先创建线程,这样就会降低处理速度。

    为了应对这种情况,我们一般会提前创建好一批线程,降低了短时间内创建和销毁线程的成本。就好比你要去火锅店吃火锅,店家不会等你来了再去屠宰场取肉,肯定会先把肉准备好。

    二、模型实现

    现在我们要建立这样一个模型,主线程作为生产者不断的向任务队列中添加任务,而线程池中的线程在不断从任务队列中拿任务。假设任务队列的容量是没有上限的。

    和阻塞队列一样,要实现这么一个模型的关键依然是 放任务PushTask 和 取任务PopTask,除此之外,在创建线程的时候,有一个需要注意的细节

    1、准备工作

    2、创建线程

    我们一次创建 4个线程,要传递给线程的参数暂定,为了方便,我们这里不主动回收线程,而是选择将线程分离。

     这里需要注意的是,pthread_create函数的第三个参数,传入的函数必须满足

    (1) 返回值是 void*

    (2) 输入的参数只有一个,那就是void*

    但是实际上,Routines是定义在类中的,是一个成员函数,参数会包含一个隐藏的this指针,所以站在编译器的角度就变成了 Routines(ThreadPool<T>* this , void* args),所以我们在函数的最开始加上 static

    成员函数一般都是放在代码区,成员函数加了static以后,此时会被放到全局区,这个时候参数就只有void*,不会包含隐藏的this指针 

    3、PushTask函数

    此时因为任务队列的容量没有上限,所以我们这里无需考虑任务队列为满的情况。_task_queue是临界资源,我们需要使用互斥锁来保护这个临界资源

    互斥锁的初始化和销毁这里就不展示了。

    还有一步我们需要等实现 PopTask函数以后再写

    4、PopTask函数

    取任务的时候,既要考虑到维护临界资源,又要考虑到任务队列为空的情况。

    (1) 既然是维护临界资源,那就需要加锁和解锁

    (2) 既然是考虑到任务队列是否为空,当任务队列为空的时候,说明消费者线程不能再继续取任务了,此时要把消费者线程加入到 条件变量中等待,当生产者线程加入任务时,再唤醒消费者线程。

    条件变量的初始化和销毁,这里就不展示了。

     PopTask函数具体实现如下:

    PushTask函数的补充:

    5、线程执行函数Routines

    现在Routines被放到了全局区,但是线程池中的线程需要访问 任务队列,因此我们需要给线程执行函数传递 this指针,这样的话,每个线程就可以访问到类成员变量了

     然后我们在线程执行函数中获得this指针,用一个名为that的指针来接收这个this指针

     我们可以写一个空的Task类,就像下面这样

     线程执行函数的完整代码如下:

     

    三、模型测试

    测试的主函数如下,主线程作为生产者线程每隔1s向任务队列添加任务。

  • 相关阅读:
    初识网络编程
    深度学习图像生成在AI去衣技术中的应用与探索
    大数据必学Java基础(六十三):COW并发容器讲解
    python_视频中语音识别转出文本
    spark的数据扩展
    倒⽴摆闭环控制的设计与开发
    OpenTiny Vue 支持 Vue2.7 啦!
    visual studio设置主题和背景颜色
    springMVC数据处理-请求,接收,乱码,json格式
    基于SM2证书实现SSL通信
  • 原文地址:https://blog.csdn.net/challenglistic/article/details/124900344