Labwork13 - ThreadPool Implementation
编译环境
一般:
System:
macOS Big Sur 11.2.3 - arm64
GNU:
Apple clang version 12.0.0 (clang-1200.0.32.29)
备用:
System:
Ubuntu 20.04 LTS - arm64
GNU:
gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)
实验内容: 设计实现一个线程池 (Thread Pool)
- 使用 Pthread API
- FIFO
- 先不考虑互斥问题
- 编译、运行、测试用例
实验过程
一、实验准备
- 原理理论
首先,我们应该了解,什么是线程池,怎样去实现一个线程池。
通过查找资料我们可以得到如下程序框图:
在本次实验中,为简单起见,我们选择实现核心线程池。
- 实验实现
数据结构:
typedef struct task // 数据结构 - 任务
{
function_t func_ptr; // 函数指针
void * args; // 函数参数
struct task * next; // 下一个任务
struct task * prev; // 上一个任务
} Task;
typedef struct // 数据结构 - 线程池
{
bool shutdown; // 是否处在叫停状态
int max_thread, num_tasks; // 线程总数和线程数
pthread_t *threads; // 线程数组指针
Task* head; // 任务队列头指针
Task* tail; // 任务队列尾指针
sem_t* queue_sem; // 阻塞状态
} ThreadPool;
实现函数:
ThreadPool * tp_create(int); // 创建线程池
bool task_enqueue(ThreadPool*, Task*); // 任务入队
Task* task_dequeue(ThreadPool*); // 任务出队
void task_empty(ThreadPool*); // 清除任务队列
bool add_task(ThreadPool*, function_t, void*); // 添加任务
void tp_destory(ThreadPool*); // 销毁线程池
void work_routine(void*); // 线程工作
实现细节的原理性分析:
本实验中有关 Pthread API 相关内容在先前实验中已经多次使用,这里不再赘述。
-
信号量
semphone.h
这一部分包括了
sem_init
、sem_wait
、sem_post
、sem_destroy
等函数,该部分的功能是实现了一个信号系统,它在各个线程均繁忙的情况下会阻塞任务队列中的内容进入线程,等待有线程空闲后再发送信号允许任务进入,通过semphone
我们实现了控制任务队列的进出从而不会产生任务得不到执行的问题。 -
互斥锁
mutex
本次实验不要求考虑互斥问题,但在实际实现过程中我们发现如果不使用互斥锁对操作进行锁定我们就会出现多个线程同时操纵线程池数据结构的问题,从而导致分支判断失效,实验失败。
我们使用pthread_mutex
实现互斥锁后,此问题得到解决。
二、实验结果
根据实验所得,通过线程池中的20个线程,1000项任务的每一项均得到了解决,认为线程池已经实现。
三、实验心得
通过本次实验,我学习到了线程池的实现原理,能够实现一个简单的线程池供任务进入并执行,在实验过程中遇到了许多的问题,在遇到问题-解决问题的不断循环中拓宽了我对线程的总体了解,增强了自身解决问题的能力,同时对于POSIX编程有了更加全面的认识。