【Linux系统编程】线程_02_同步sync_05_阻塞队列实现生产者消费者模型

main线程
1. 创建线程池
  
2. 主线程往阻塞队列中放任务; 子线程从阻塞队列中取任务,然后执行任务

3. 线程池优雅地退出

4. 销毁线程池
// BlockQ.h
#ifndef __WD_BLOCKQ_H
#define __WD_BLOCKQ_H

#include <func.h>

#define N 1024

typedef int E;

// 循环数组实现队列
typedef struct {
    E elements[N];
    int front;
    int rear;
    int size;

    pthread_mutex_t mutex;
    pthread_cond_t  not_empty;  // 非空
    pthread_cond_t  not_full;   // 不满
} BlockQ;

// API
BlockQ* blockq_create(void);
void blockq_destroy(BlockQ* q);

void blockq_push(BlockQ* q, E val);
E    blockq_pop(BlockQ* q);
E    blockq_peek(BlockQ* q);
bool blockq_empty(BlockQ* q);
bool blockq_full(BlockQ* q);

#endif
// BlockQ.c
#include "BlockQ.h"

// 创建空的阻塞队列
BlockQ* blockq_create(void) {
    BlockQ* q = (BlockQ*)calloc(1, sizeof(BlockQ));

    pthread_mutex_init(&q->mutex, NULL);
    pthread_cond_init(&q->not_empty, NULL);
    pthread_cond_init(&q->not_full, NULL);

    return q;
}

void blockq_destroy(BlockQ* q) {
    pthread_mutex_destroy(&q->mutex);
    pthread_cond_destroy(&q->not_empty);
    pthread_cond_destroy(&q->not_full);

    free(q);
}

void blockq_push(BlockQ* q, E val) {
    // 获取锁
    pthread_mutex_lock(&q->mutex);
    // 注意事项:一定要写成 while, 需要再次检查条件是否满足
    while (q->size == N) {  // 满了等等  
        // 1. 释放q->mutex
        // 2. 陷入阻塞状态
        // 3. 当pthread_cond_wait返回时,一定再一次获取了q->mutex
        // 语义:返回时,条件曾经成立过,现在是否成立,不确定;需要再一次检查
        // 存在虚假唤醒现象
        // 队列满时,线程等待,直到其他线程发出not_full
        pthread_cond_wait(&q->not_full, &q->mutex);
    } // a. 获取了q->mutex; b. q->size != N

    q->elements[q->rear] = val;
    q->rear = (q->rear + 1) % N;
    q->size++;

    // not_empty条件成立, 唤醒等待not_empty条件的线程
    pthread_cond_signal(&q->not_empty);
    
    pthread_mutex_unlock(&q->mutex);
}

E blockq_pop(BlockQ* q) {
    pthread_mutex_lock(&q->mutex);
    while (q->size == 0) {  // 空了等等
        pthread_cond_wait(&q->not_empty, &q->mutex);
    }
    // a. 获取了q->mutex; b. q->size != 0
    E retval = q->elements[q->front];
    q->front = (q->front + 1) % N;
    q->size--;

    // not_full条件成立,唤醒等待not_full条件的线程
    pthread_cond_signal(&q->not_full);

    pthread_mutex_unlock(&q->mutex);

    return retval;
}

E blockq_peek(BlockQ* q) {
    pthread_mutex_lock(&q->mutex);
    while (q->size == 0) {
        pthread_cond_wait(&q->not_empty, &q->mutex);
    }
    // a. 获取了q->mutex; b. q->size != 0;
    E retval = q->elements[q->front];
    pthread_mutex_unlock(&q->mutex);
    return retval;
}

bool blockq_full(BlockQ* q) {
    pthread_mutex_lock(&q->mutex);
    int size = q->size;
    pthread_mutex_unlock(&q->mutex);
    return size == N;
}

bool blockq_empty(BlockQ* q) {
    pthread_mutex_lock(&q->mutex);
    int size = q->size;
    pthread_mutex_unlock(&q->mutex);
    return size == 0;
}
// pc.c
#include <func.h>
#include "BlockQ.h"

typedef struct {
    pthread_t* threads;
    int nums;   // 线程数目
    BlockQ* q;  // 阻塞队列
} ThreadPool;

void* start_routine(void* args) {
    ThreadPool* pool = (ThreadPool*) args;
    pthread_t tid = pthread_self();

    while (1) {
        // 从阻塞队列中获取任务
        E task_id = blockq_pop(pool->q);
        if (task_id == -1) {    // -1:退出任务
            pthread_exit(NULL);
        }
        printf("%lx: execute task %d\n", tid, task_id);
        sleep(3);   // 模拟执行任务
        printf("%lx: task %d done\n", tid, task_id);
    }

    return NULL;
}

ThreadPool* threadpool_create(int n) {
    ThreadPool* pool = (ThreadPool*)malloc(sizeof(ThreadPool));

    pool->threads = (pthread_t*)malloc(n * sizeof(pthread_t));
    pool->nums = n;
    pool->q = blockq_create();
    // 创建线程
    for(int i = 0; i < n; i++) {
        pthread_create(pool->threads + i, NULL, start_routine, (void*)pool);
    }

    return pool;
}

void threadpool_destroy(ThreadPool* pool) {
    blockq_destroy(pool->q);
    free(pool->threads);
    free(pool);
}

// gcc pc.c BlockQ.c -o pc -Wall -g -lpthread
int main(int argc, char* argv[])
{
    // 1. 创建线程池, 并初始化
    ThreadPool* pool = threadpool_create(8);

    // 2. 主线程生产任务
    for(int i = 0; i < 100; i++) {
        blockq_push(pool->q, i + 1);
    }

    sleep(5);

    // 3.a 暴力退出线程池
    /* for (int i = 0; i < 8; i++) {
        pthread_cancel(pool->threads[i]);
    } */

    // 3.b 优雅退出线程池
    for (int i = 0; i < pool->nums; i++) {
        blockq_push(pool->q, -1); // -1表示退出任务
    }

    for(int i = 0; i < pool->nums; i++) {
        pthread_join(pool->threads[i], NULL);
    }

    // 4. 销毁线程池
    threadpool_destroy(pool);
    
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值