Linux杂谈: 实现一种简单实用的线程池(C语言)
基本功能
1. 实现一个线程的队列,队列中的线程启动后不再释放;
2. 没有任务执行时,线程处于pending状态,等待唤醒,不占cpu;
3. 当有任务需要执行时,从线程队列中取出一个线程执行任务;
4. 任务执行完成后线程再次进入pending状态,等待唤醒;
扩展功能
1. 线程的队列大小可设置;
2. 最大可创建的线程数可设置;
3. 根据运行需求,按需步进启动线程,避免大量线程一直处于pending状态,占用资源;
关键代码分析
数据结构
1 /* 线程执行的任务参数 */
2 typedef struct
3 {
4 void (*func)(void*, void*); /* 任务函数指针 */
5 void *arg1; /* 任务函数第一个参数 */
6 void *arg2; /* 任务函数第二个参数 */
7 }tThreadTaskInfo;
8
9 /* 线程池参数 */
10 typedef struct
11 {
12 pthread_mutex_t lock; /* 线程池互斥锁 */
13 pthread_cond_t cond; /* 线程池同步信号 */
14
15 pthread_t *threads; /* 保存线程池创建的所有线程 */
16 int32_t threadMaxNum; /* 最大可创建线程数 */
17 int32_t threadStartStep; /* 一次启动线程的个数 */
18 int32_t threadStartCnt; /* 已启动线程个数 */
19 int32_t threadPendCnt; /* 已启动但是处于Pending状态的线程 */
20
21 tThreadTaskInfo *taskQueue; /* 等待执行的任务队列 */
22 int32_t taskQueueSize; /* 任务队列的大小 */
23 int32_t taskQueueHead; /* 当前任务队列头索引 */
24 int32_t taskQueueTail; /* 当前任务队列尾索引 */
25 int32_t taskPendCnt; /* 等待执行的任务个数 */
26
27 int32_t isShutdown; /* 线程池正在关闭 */
28 }tThreadpoolInfo;
创建线程池
- 创建线程池时只分配了存储pthread_t的空间,但是不启动线程,后面根据需求步进启动;
1 /************************************
2 * 创建线程池
3 *
4 * @threadMaxNum -- 最大可创建线程个数
5 * @threadStartStep -- 一次启动线程的个数
6 * @taskQueueSize -- 任务队列的大小
7 *
8 * @Retuen -- 成功:线程池的引用
9 * 失败:NULL
10 * **********************************/
11 tThreadpoolInfo* threadpool_create(
12 int32_t threadMaxNum,
13 int32_t threadStartStep,
14 int32_t taskQueueSize)
15 {
16 tThreadpoolInfo *threadpool = NULL;
17
18 if ((0 >= threadMaxNum)
19 || (0 >= threadStartStep)
20 || (0 >= taskQueueSize))
21 {
22 THREADPOOL_ERR("invalid param.\r\n");
23 goto error_exit;
24 }
25
26 threadpool = (tThreadpoolInfo *)malloc(sizeof(tThreadpoolInfo));
27 if (NULL == threadpool)
28 {
29 THREADPOOL_ERR("malloc threadpool failed.\r\n");
30 goto error_exit;
31 }
32
33 memset(threadpool, 0, sizeof(tThreadpoolInfo));
34 threadpool->threadMaxNum = threadMaxNum;
35 threadpool->threadStartStep = threadStartStep;
36 threadpool->taskQueueSize = taskQueueSize;
37
38 /* 分配线程存储资源 */
39 threadpool->threads = (pthread_t *)calloc(threadMaxNum, sizeof(pthread_t));
40 if (NULL == threadpool->threads)
41 {
42 THREADPOOL_ERR("malloc threads failed.\r\n");
43 goto error_exit;
44 }
45
46 /* 分配任务队列 */
47 threadpool->taskQueue = (tThreadTaskInfo *)calloc(taskQueueSize, sizeof(tThreadTaskInfo));
48 if (NULL == threadpool->taskQueue)
49 {
50 THREADPOOL_ERR("malloc task queue failed.\r\n");
51 goto error_exit;
52 }
53
54 /* 初始化互斥信号量和同步信号 */
55 if (0 != THREADPOOL_LOCK_INIT(threadpool))
56 {
57 THREADPOOL_ERR("mutex init failed.\r\n");
58 goto error_exit;
59 }
60
61 if (0 != THREADPOOL_COND_INIT(threadpool))
62 {
63 THREADPOOL_ERR("cond init failed.\r\n");
64 goto error_exit;
65 }
66
67 return threadpool;
68
69 error_exit:
70
71 if (threadpool != NULL)
72 {
73 threadpool_free(threadpool);
74 }
75
76 return NULL;
77 }
向线程池添加任务
- 查看等待队列是否有空闲,如果没有空闲则返回错误;
- 查看当前有没有处于pending的线程,如果没有则按照步进启动新的线程,如果已达到最大线程数则返回错误;
- 将任务添加到队列中,并唤醒一个线程执行任务;
1 /************************************
2 * 向线程池添加任务
3 *
4 * @threadpool -- 线程池引用
5 * @taskfunc -- 任务回调函数
6 * @arg1 -- 任务第一个参数
7 * @arg1 -- 任务第二个参数
8 *
9 * @Return -- 成功: 0
10 * 失败: -1
11 * **********************************/
12 int32_t threadpool_addtask(
13 tThreadpoolInfo *threadpool,
14 THREADPOOLTASKFUNC taskfunc,
15 void *arg1,
16 void *arg2)
17 {
18 int32_t ret = 0;
19
20 if ((NULL == threadpool) || (NULL == taskfunc))
21 {
22 THREADPOOL_ERR("invalid param.\r\n");
23 return -1;
24 }
25
26 THREADPOOL_LOCK(threadpool);
27
28 do
29 {
30 if (threadpool->isShutdown)
31 {
32 THREADPOOL_ERR("threadpool is shutdown.\r\n");
33 ret = -1;
34 break;
35 }
36
37 /* 判断等待执行的任务队列是否满 */
38 if (threadpool->taskPendCnt == threadpool->taskQueueSize)
39 {
40 THREADPOOL_ERR("task queue is full.\r\n");
41 ret = -1;
42 break;
43 }
44
45 /* 如果pending状态的线程已用完,则启动新的线程 */
46 if (threadpool->threadPendCnt <= 0)
47 {
48 if (0 != threadpool_start(threadpool))
49 {
50 ret = -1;
51 break;
52 }
53 }
54
55 /* 将任务放入对尾 */
56 threadpool->taskQueue[threadpool->taskQueueTail].func = taskfunc;
57 threadpool->taskQueue[threadpool->taskQueueTail].arg1 = arg1;
58 threadpool->taskQueue[threadpool->taskQueueTail].arg2 = arg2;
59
60 threadpool->taskQueueTail = (threadpool->taskQueueTail + 1) % threadpool->taskQueueSize;
61 threadpool->taskPendCnt++;
62
63 /* 唤醒一个线程执行任务 */
64 THREADPOOL_COND_SIGNAL(threadpool);
65
66 } while(0);
67
68 THREADPOOL_UNLOCK(threadpool);
69 return ret;
70 }
线程的回调函数
- 线程第一次启动和被唤醒后检查队列中是否有需要执行的任务,如果没有则继续等待唤醒;
- 如果有需要执行的任务,则从队列中取一个任务并执行;
- 如果线程池已销毁,则退出线程;
1 /************************************
2 * 线程回调函数
3 * 等待线程池分配任务并执行分配的任务
4 *
5 * @arg -- 线程池引用
6 * **********************************/
7 void* thread_callback(void *arg)
8 {
9 tThreadpoolInfo *threadpool = (tThreadpoolInfo *)arg;
10 tThreadTaskInfo task;
11
12 while (1)
13 {
14 THREADPOOL_LOCK(threadpool);
15
16 /* 等待任务分配的信号
17 * 如果当前没有等待执行的任务,并且线程池没有关闭则继续等待信号 */
18 while ((0 == threadpool->taskPendCnt)
19 && (0 == threadpool->isShutdown))
20 {
21 THREADPOOL_COND_WAIT(threadpool);
22 }
23
24 /* 如果线程池已关闭,则退出线程 */
25 if (threadpool->isShutdown)
26 break;
27
28 /* 取任务队列中当前第一个任务 */
29 task.func = threadpool->taskQueue[threadpool->taskQueueHead].func;
30 task.arg1 = threadpool->taskQueue[threadpool->taskQueueHead].arg1;
31 task.arg2 = threadpool->taskQueue[threadpool->taskQueueHead].arg2;
32
33 threadpool->taskQueueHead = (threadpool->taskQueueHead + 1) % threadpool->taskQueueSize;
34 threadpool->taskPendCnt--;
35 threadpool->threadPendCnt--;
36
37 THREADPOOL_UNLOCK(threadpool);
38
39 /* 执行任务 */
40 (*(task.func))(task.arg1, task.arg2);
41
42 /* 任务执行完成后,线程进入pending状态 */
43 THREADPOOL_LOCK(threadpool);
44 threadpool->threadPendCnt++;
45 THREADPOOL_UNLOCK(threadpool);
46 }
47
48 threadpool->threadStartCnt--;
49 THREADPOOL_UNLOCK(threadpool);
50
51 pthread_exit(NULL);
52 }
线程池销毁
- 销毁为确保资源释放,需要唤醒所有线程,并等待所有线程退出;
1 /************************************
2 * 删除线程池
3 *
4 * @threadpool -- 线程池引用
5 * **********************************/
6 int32_t threadpool_destroy(tThreadpoolInfo *threadpool)
7 {
8 int32_t ret = 0;
9 int32_t i = 0;
10
11 if (NULL == threadpool)
12 {
13 THREADPOOL_ERR("invalid param.\r\n");
14 return -1;
15 }
16
17 THREADPOOL_LOCK(threadpool);
18
19 do
20 {
21 if (threadpool->isShutdown)
22 {
23 THREADPOOL_UNLOCK(threadpool);
24 break;
25 }
26
27 threadpool->isShutdown = 1;
28
29 /* 唤醒所有线程 */
30 if (0 != THREADPOOL_COND_BROADCAST(threadpool))
31 {
32 THREADPOOL_ERR("cond broadcast failed.\r\n");
33 threadpool->isShutdown = 0;
34 continue;
35 }
36
37 THREADPOOL_UNLOCK(threadpool);
38
39 /* 等待所有进程退出 */
40 for (i = 0; i < threadpool->threadStartCnt; i++)
41 {
42 pthread_cancel(threadpool->threads[i]);
43 pthread_join(threadpool->threads[i], NULL);
44 }
45
46 }while(0);
47
48 if (0 != ret)
49 {
50 threadpool->isShutdown = 0;
51 return ret;
52 }
53
54 threadpool_free(threadpool);
55 return ret;
56 }
线程池测试
- 创建最大线程数=256,队列大小=64,启动步进=8 的线程池;
- 向线程池添加1024个任务,如果添加失败则等待1秒再添加;
- 验证1024个任务是否均能执行;
1 /***********************************
2 * Filename : test_main.c
3 * Author : taopeng
4 * *********************************/
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10
11 #include "threadpool.h"
12
13 void test_task(void *arg)
14 {
15 long id = (long)arg;
16
17 printf("task[%ld] enter\r\n", id);
18 sleep(3);
19
20 return;
21 }
22
23 int32_t main(int32_t argc, char *argv[])
24 {
25 tThreadpoolInfo *threadpool;
26 long id;
27
28 threadpool = threadpool_create(128, 8, 64);
29 if (NULL == threadpool)
30 return -1;
31
32 for (id = 1; id <= 1024;)
33 {
34 if (0 != threadpool_addtask(threadpool, (THREADPOOLTASKFUNC)test_task, (void *)id, NULL))
35 {
36 sleep(1);
37 continue;
38 }
39
40 id++;
41 }
42
43 sleep(30);
44
45 threadpool_destroy(threadpool);
46 return 0;
47 }
代码实例链接
https://gitee.com/github-18274965/threadpool.git
Linux杂谈: 实现一种简单实用的线程池(C语言)的更多相关文章
- asp.net core C#设计一个实用的线程池
菜菜呀,我最近研究技术呢,发现线上一个任务程序线程数有点多呀 CEO,CTO,CFO于一身的CXO x总,你学编程呢? 菜菜 作为公司总负责人,我以后还要管理技术部门呢,怎么能不会技术呢 CEO,CT ...
- Linux下好用的简单实用命令
1.你是否为在输入了一大串命令之后发现第一个字符打错了而苦恼?只能删除重来嘛?或者一步步左移光标? NO,一个组合键轻松搞定 Ctrl+A -----到命令行首 Ctrl+E ------到命令行末 ...
- jQuery的几种简单实用效果
许久未分享博客,或许已生疏. 闲来无事, 分享几个jQuery简单实用的效果案例 不喜勿喷... 1.页面常用的返回顶部 <!DOCTYPE html> <html lang=&qu ...
- iOS边练边学--多线程介绍、NSThread的简单实用、线程安全以及线程之间的通信
一.iOS中的多线程 多线程的原理(之前多线程这块没好好学,之前对多线程的理解也是错误的,这里更正,好好学习这块) iOS中多线程的实现方案有以下几种 二.NSThread线程类的简单实用(直接上代码 ...
- 两种unix网络编程线程池的设计方法
unp27章节中的27.12中,我们的子线程是通过操作共享任务缓冲区,得到task的,也就是通过线程间共享的clifd[]数组,这个数组其实就是我们的任务数组,得到其中的connfd资源. 我们对这个 ...
- 一个简单的python线程池框架
初学python,实现了一个简单的线程池框架,线程池中除Wokers(工作线程)外,还单独创建了一个日志线程,用于日志的输出.线程间采用Queue方式进行通信. 代码如下:(不足之处,还请高手指正) ...
- 自定义简单版本python线程池
python未提供线程池模块,在python3上用threading和queue模块自定义简单线程池,代码如下: #用threading queue 做线程池 import queue import ...
- 简单实现java线程池
使用多线程以及线程池的意义无需多说,要想掌握线程池,最好的方法还是自己手动去实现. 一.实现思路 (网络盗图) 二.实现代码 1.线程池类 package com.ty.thread; im ...
- java线程的3种实现方式及线程池
1 准备数据 1.1 目标 为了形象地演示线程的工作现象, 准备两个文件datas/odds.txt和datas/evens.txt, 分别存储奇数和偶数, 内容如下: odds.txt 1 3 5 ...
随机推荐
- Java死锁编码及定位分析的demo
死锁 死锁是什么 大学课程中的四个要素: (1)互斥(2)不可抢占(3)循环等待(4)请求保持 也就是下图所描述 产生死锁的主要原因 (1)系统资源不足(2)进程运行推进的顺序不合适(3)资源分配不当 ...
- mysql update 子查询作为条件
UPDATE t_commission_template tctJOIN ( SELECT max(id) maxid FROM t_commission_template WHERE taken_m ...
- 加密sqlite3数据库文件
目录 EncryptSqlite3 实现原理 使用方法 不足之处 GitHub地址 EncryptSqlite3 加密sqlite3数据库,产生的数据库文件别人打不开. 实现原理 在写入文件前对每个字 ...
- elasticsearch要点及常用查询
目录 elasticsearch要点及常用查询 查询与过滤 明确查询和过滤各自的优缺点,以及适用场景. 性能上的差异 适用场景 1.kibana 中操作es-查询 Mapping映射基础 mappin ...
- Jmeter之参数化函数助手__randomstring
1.Tools->函数助手对话框,选择__Random String,2表示随机生成的字符长度:3表示从哪些字符中随机生成:然后点击生成,得到对应的变量: 5中372表示该函数随机生成的字符串, ...
- linux(centos8):使用zip/unzip压缩和解压缩文件
一,查看zip命令所属的rpm包 1,zip [root@kubemaster ~]# whereis zip zip: /usr/bin/zip /usr/share/man/man1/zip.1. ...
- S10 TES的八强赛,创造了奇迹,看看比赛时网友们怎么说的
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理 我相信很多观众会和我一样吧,当TES赢下第一局后开始心怀侥幸,赢下第二局后觉 ...
- JavaSE学习笔记03流程控制
Java流程控制 1.顺序结构 java的基本结构就是顺序结构,除非特别指明,否则就按照顺序一句一句往下执行. 顺序结构是最简单的算法结构,它是任何一个算法都离不开的一种基本算法结构. 2. 选择结构 ...
- C++ 多线程 std::thread 使用总结
在C++ 11之前,官方并没有支持线程库.C++ 11通过标准库引入了对 thread 类的支持,大大方便了完成多线程开发的工作. std::thread 构造函数 (1)thread() noex ...
- jqgrid与bootstrap样式结合问题
还有个问题,就是 <link rel="stylesheet" href="../boot/grid/ui.jqgrid.css" type=" ...