线程池见解

线程池

线程池是一种用于管理和优化多线程应用程序性能的设计模式。它预先创建一组线程,并将任务分配给这些线程执行,从而减少了创建和销毁线程的开销,提高了系统的效率和响应速度。

线程池的基本概念

  1. 线程池:一个包含多个线程的集合,这些线程可以被重复使用来执行多个任务。
  2. 任务队列:一个存储等待执行任务的队列。当有新的任务到来时,它们会被添加到这个队列中。
  3. 工作线程:线程池中的线程,这些线程会不断从任务队列中取出任务并执行。
  4. 线程池管理器:负责管理线程池的生命周期,包括线程的创建、销毁和任务的分配。

线程池的优点

  1. 减少线程创建和销毁的开销:线程的创建和销毁是昂贵的操作。线程池通过重用线程减少了这些开销。
  2. 提高响应速度:当任务到来时,不需要等待线程创建,可以立即使用线程池中的空闲线程执行任务。
  3. 控制并发量:可以通过限制线程池中的最大线程数来控制系统的并发量,防止资源耗尽。
  4. 统一管理:线程池提供了一个统一的管理接口,可以更方便地监控和调优线程的使用。

线程池的实现原理

线程池的实现通常包括以下几个部分:

  1. 线程池初始化:创建一个固定数量的线程并将它们放入线程池中。

  2. 任务提交:提供一个接口,将任务添加到任务队列中。

  3. 任务调度:线程池中的工作线程不断从任务队列中取出任务并执行。

  4. 线程管理:根据系统负载动态调整线程池的大小(可选)。

线程示意图

以下是一个示意图,展示了线程池的基本结构和工作流程:

+-------------------------+
|      Thread Pool        |
|                         |
|  +-------------------+  |
|  |   Worker Thread   |  |
|  |    (Thread 1)     |  |
|  +-------------------+  |
|                         |
|  +-------------------+  |
|  |   Worker Thread   |  |
|  |    (Thread 2)     |  |
|  +-------------------+  |
|                         |
|  +-------------------+  |
|  |   Worker Thread   |  |
|  |    (Thread 3)     |  |
|  +-------------------+  |
|                         |
|  +-------------------+  |
|  |   Worker Thread   |  |
|  |    (Thread 4)     |  |
|  +-------------------+  |
|                         |
+-------------------------+

+-------------------------+
|      Task Queue         |
|                         |
|  +-------------------+  |
|  |       Task 1      |  |
|  +-------------------+  |
|                         |
|  +-------------------+  |
|  |       Task 2      |  |
|  +-------------------+  |
|                         |
|  +-------------------+  |
|  |       Task 3      |  |
|  +-------------------+  |
|                         |
|  +-------------------+  |
|  |       Task 4      |  |
|  +-------------------+  |
|                         |
+-------------------------+

图片说明

  1. 线程池 (Thread Pool)
    • 包含多个工作线程(Worker Thread),如图中的 Thread 1 到 Thread 4。
    • 这些工作线程在创建后会一直存在,并等待任务的到来。
  2. 任务队列 (Task Queue)
    • 存储待执行的任务,如图中的 Task 1 到 Task 4。
    • 当有新的任务到来时,它们会被添加到这个队列中。

工作流程

  1. 任务提交
    • 新的任务被提交到任务队列中。
  2. 任务分配
    • 线程池中的工作线程不断地从任务队列中取出任务并执行。
    • 每个工作线程在完成当前任务后,会继续从队列中取出下一个任务,直到任务队列为空。
  3. 任务执行
    • 工作线程执行任务函数,并处理任务参数。
  4. 线程重用
    • 工作线程在完成任务后不会被销毁,而是继续等待新的任务到来,从而实现线程的重用。

优点

  • 减少线程创建和销毁的开销:通过重用线程,减少了频繁创建和销毁线程的开销。
  • 提高响应速度:任务可以立即由空闲线程执行,而不需要等待新线程的创建。
  • 控制并发量:通过限制线程池中的最大线程数,控制系统的并发量,防止资源耗尽。

线程池的基本操作

线程池相关头文件,宏定义以及结构体,包含线程池的所有状态信息,包括线程数组、任务队列、锁和条件变量

 /****************************************************************
  *
  *	name	 :	threadpool
  *	function :  创建一个线程池,其中有四个线程,将任务分配给线程,完成任务可以回收线程,也可以添加线程,也可以销毁线程
  *	argument :  
  *	retval	 :  None
  *	author	 :  yq_dyx@163.com
  *	date	 :  2024/06/11
  * note	 :  None
  * 	
 ****************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define MAX_THREADS 4  // 线程池中的最大线程数
#define MAX_QUEUE 10   // 任务队列的最大大小

// 定义任务结构体
typedef struct {
    void (*function)(void*);  // 任务函数指针
    void* argument;           // 任务函数参数
} task_t;

// 定义线程池结构体
typedef struct {
    pthread_mutex_t lock;          // 互斥锁
    pthread_cond_t notify;         // 条件变量
    pthread_t threads[MAX_THREADS]; // 线程数组
    task_t queue[MAX_QUEUE];       // 任务队列
    int queue_size;                // 任务队列大小
    int head;                      // 队列头索引
    int tail;                      // 队列尾索引
    int count;                     // 队列中任务计数
    int shutdown;                  // 线程池关闭标志
} threadpool_t;

工作线程函数

线程池中每个线程执行的函数,从任务队列中取出任务并执行,如果线程池关闭,线程进入等待状态,如果线程池关闭,线程退出.

void* thread_do_work(void* arg) {
    threadpool_t* pool = (threadpool_t*)arg;  // 获取线程池指针
    task_t task;

    while (1) {
        pthread_mutex_lock(&(pool->lock));  // 加锁

        // 等待任务队列中有任务或线程池关闭
        while ((pool->count == 0) && (!pool->shutdown)) {
            pthread_cond_wait(&(pool->notify), &(pool->lock));
        }

        // 如果线程池关闭,退出线程
        if (pool->shutdown) {
            pthread_mutex_unlock(&(pool->lock));
            pthread_exit(NULL);
        }

        // 从任务队列中取出任务
        task.function = pool->queue[pool->head].function;
        task.argument = pool->queue[pool->head].argument;
        pool->head = (pool->head + 1) % MAX_QUEUE;
        pool->count -= 1;

        pthread_mutex_unlock(&(pool->lock));  // 解锁

        // 执行任务
        (*(task.function))(task.argument);
    }

    pthread_exit(NULL);
    return NULL;
}

线程池创建函数

  • 初始化线程池结构体。
  • 初始化互斥锁和条件变量。
  • 创建工作线程
// 创建线程池
threadpool_t* threadpool_create() {
    threadpool_t* pool = (threadpool_t*)malloc(sizeof(threadpool_t));
    if (pool == NULL) {
        return NULL;  // 分配内存失败
    }

    pool->queue_size = MAX_QUEUE;
    pool->head = pool->tail = pool->count = 0;
    pool->shutdown = 0;

    // 初始化互斥锁和条件变量
    pthread_mutex_init(&(pool->lock), NULL);
    pthread_cond_init(&(pool->notify), NULL);

    // 创建工作线程
    for (int i = 0; i < MAX_THREADS; i++) {
        pthread_create(&(pool->threads[i]), NULL, thread_do_work, (void*)pool);
    }

    return pool;
}

添加任务函数

  • 向任务队列中添加任务。
  • 如果任务队列已满,返回错误。
  • 通知工作线程有新任务。
// 向线程池添加任务
int threadpool_add(threadpool_t* pool, void (*function)(void*), void* argument) {
    pthread_mutex_lock(&(pool->lock));  // 加锁

    int next = (pool->tail + 1) % pool->queue_size;

    // 如果任务队列已满,返回错误
    if (pool->count == pool->queue_size) {
        pthread_mutex_unlock(&(pool->lock));
        return -1;  // 任务队列已满
    }

    // 添加任务到任务队列
    pool->queue[pool->tail].function = function;
    pool->queue[pool->tail].argument = argument;
    pool->tail = next;
    pool->count += 1;

    // 通知工作线程有新任务
    pthread_cond_signal(&(pool->notify));
    pthread_mutex_unlock(&(pool->lock));  // 解锁

    return 0;
}

销毁线程池函数

  • 设置关闭标志,通知所有工作线程。
  • 等待所有工作线程结束。
  • 销毁互斥锁和条件变量,释放线程池内存。
// 销毁线程池
void threadpool_destroy(threadpool_t* pool) {
    pthread_mutex_lock(&(pool->lock));  // 加锁
    pool->shutdown = 1;  // 设置关闭标志
    pthread_cond_broadcast(&(pool->notify));  // 通知所有工作线程
    pthread_mutex_unlock(&(pool->lock));  // 解锁

    // 等待所有工作线程结束
    for (int i = 0; i < MAX_THREADS; i++) {
        pthread_join(pool->threads[i], NULL);
    }

    // 销毁互斥锁和条件变量
    pthread_mutex_destroy(&(pool->lock));
    pthread_cond_destroy(&(pool->notify));
    free(pool);  // 释放线程池内存
}

示例任务函数

模拟任务执行,打印任务信息并休眠1秒

// 示例任务函数
void example_task(void* arg) {
    int num = *(int*)arg;
    printf("Thread %lu is working on task %d\n", pthread_self(), num);
    sleep(1);  // 模拟任务执行时间
}

主函数

  • 创建线程池。
  • 向线程池添加任务。
  • 等待所有任务完成。
  • 销毁线程池。
int main() {
    // 创建线程池
    threadpool_t* pool = threadpool_create();
    if (pool == NULL) {
        fprintf(stderr, "Failed to create thread pool\n");
        return 1;
    }

    // 向线程池添加任务
    int tasks[20];
    for (int i = 0; i < 20; i++) {
        tasks[i] = i;
        threadpool_add(pool, example_task, (void*)&tasks[i]);
    }

    sleep(5);  // 等待所有任务完成
    threadpool_destroy(pool);  // 销毁线程池

    return 0;
}

代码详细说明

  1. 任务结构体 task_t
    • function:指向任务函数的指针。
    • argument:任务函数的参数。
  2. 线程池结构体 threadpool_t
    • lock:互斥锁,用于保护共享资源。
    • notify:条件变量,用于通知工作线程有新任务到来。
    • threads:线程数组,存储线程池中的线程。
    • queue:任务队列,存储待执行的任务。
    • queue_size:任务队列的大小。
    • head:任务队列的头索引。
    • tail:任务队列的尾索引。
    • count:任务队列中的任务计数。
    • shutdown:线程池关闭标志。
  3. 工作线程函数 thread_do_work
    • 从任务队列中取出任务并执行。
    • 如果任务队列为空且线程池未关闭,线程进入等待状态。
    • 如果线程池关闭,线程退出。
  4. 线程池创建函数 threadpool_create
    • 初始化线程池结构体。
    • 初始化互斥锁和条件变量。
    • 创建工作线程。
  5. 添加任务函数 threadpool_add
    • 向任务队列中添加任务。
    • 如果任务队列已满,返回错误。
    • 通知工作线程有新任务。
  6. 销毁线程池函数 threadpool_destroy
    • 设置关闭标志,通知所有工作线程。
    • 等待所有工作线程结束。
    • 销毁互斥锁和条件变量,释放线程池内存。
  7. 示例任务函数 example_task
    • 模拟任务执行,打印任务信息并休眠1秒。
  8. 主函数 main
    • 创建线程池。
    • 向线程池添加任务。
    • 等待所有任务完成。
    • 销毁线程池。
posted @   不懂小白在线记录  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示