基本功能

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语言)的更多相关文章

  1. asp.net core C#设计一个实用的线程池

    菜菜呀,我最近研究技术呢,发现线上一个任务程序线程数有点多呀 CEO,CTO,CFO于一身的CXO x总,你学编程呢? 菜菜 作为公司总负责人,我以后还要管理技术部门呢,怎么能不会技术呢 CEO,CT ...

  2. Linux下好用的简单实用命令

    1.你是否为在输入了一大串命令之后发现第一个字符打错了而苦恼?只能删除重来嘛?或者一步步左移光标? NO,一个组合键轻松搞定 Ctrl+A -----到命令行首 Ctrl+E ------到命令行末 ...

  3. jQuery的几种简单实用效果

    许久未分享博客,或许已生疏. 闲来无事, 分享几个jQuery简单实用的效果案例 不喜勿喷... 1.页面常用的返回顶部 <!DOCTYPE html> <html lang=&qu ...

  4. iOS边练边学--多线程介绍、NSThread的简单实用、线程安全以及线程之间的通信

    一.iOS中的多线程 多线程的原理(之前多线程这块没好好学,之前对多线程的理解也是错误的,这里更正,好好学习这块) iOS中多线程的实现方案有以下几种 二.NSThread线程类的简单实用(直接上代码 ...

  5. 两种unix网络编程线程池的设计方法

    unp27章节中的27.12中,我们的子线程是通过操作共享任务缓冲区,得到task的,也就是通过线程间共享的clifd[]数组,这个数组其实就是我们的任务数组,得到其中的connfd资源. 我们对这个 ...

  6. 一个简单的python线程池框架

    初学python,实现了一个简单的线程池框架,线程池中除Wokers(工作线程)外,还单独创建了一个日志线程,用于日志的输出.线程间采用Queue方式进行通信. 代码如下:(不足之处,还请高手指正) ...

  7. 自定义简单版本python线程池

    python未提供线程池模块,在python3上用threading和queue模块自定义简单线程池,代码如下: #用threading queue 做线程池 import queue import ...

  8. 简单实现java线程池

    使用多线程以及线程池的意义无需多说,要想掌握线程池,最好的方法还是自己手动去实现. 一.实现思路      (网络盗图) 二.实现代码 1.线程池类 package com.ty.thread; im ...

  9. java线程的3种实现方式及线程池

    1 准备数据 1.1 目标 为了形象地演示线程的工作现象, 准备两个文件datas/odds.txt和datas/evens.txt, 分别存储奇数和偶数, 内容如下: odds.txt 1 3 5 ...

随机推荐

  1. thinkphp6.0.x 反序列化详记(二)

    前言 接上文找第二条POP链. 环境配置 同上文 POP链构造 寻找__destruct方法 仍然是寻找__destruct,这次关注AbstractCache.php(/vendor/league/ ...

  2. Rust之路(3)——数据类型 下篇

    [未经书面同意,严禁转载] -- 2020-10-14 -- 架构是道,数据是术.道可道,非常道:术不名,不成术!道无常形,术却可循规. 学习与分析数据类型,最基本的方法就是搞清楚其存储原理,变量和对 ...

  3. Rust之路(1)

    [未经书面许可,严禁转载]-- 2020-10-09 -- 正式开始Rust学习之路了! 思而不学则罔,学而不思则殆.边学边练才能快速上手,让我们先来个Hello World! 但前提是有Rust环境 ...

  4. 盐城5138.6118(薇)xiaojie:盐城哪里有xiaomei

    盐城哪里有小姐服务大保健[微信:5138.6118倩儿小妹[盐城叫小姐服务√o服务微信:5138.6118倩儿小妹[盐城叫小姐服务][十微信:5138.6118倩儿小妹][盐城叫小姐包夜服务][十微信 ...

  5. day07 Pyhton学习

    一.昨日内容回顾 小数据池,常量池 id()内存地址 is == 的区别 is 判断的是内存地址 == 判断的是值 存在的意义: 快速的创建字符串,整数,布尔值的对象 帮你节省内存 解码和编码 enc ...

  6. .net c#后台请求接口

    我们在请求接口的时候,有时因为跨域的问题,总是请求接口失败,亦或是请求接口时,页面还存在跳转的问题,这个时候,我们通过前台ajax请求自己的一般处理程序,用一般处理程序请求客户提供的接口 //获取to ...

  7. 50种编程语言,一句 “Hello, World”!展现编程语言七十年发展!

    mod confinment { use std::os::raw::{c_char}; extern "C" { pub fn puts(txt: *const c_char); ...

  8. 联赛%你测试10T2:漫无止境的八月

    题意: 思路: 有几个特殊的性质: 在不考虑q里面的单点修改,我们先只判断一个序列是否Yes. 我们注意到每次操作都是对一个长度为k的区间进行区间加减1的操作,所以我们如果将序列里面的数按%k分组,把 ...

  9. centos8用firewalld搭建防火墙

    一,firewalld的systemd管理命令 启动:systemctl start firewalld 关闭:systemctl stop firewalld 查看状态:systemctl stat ...

  10. Linux系统及第三方应用官方文档

    通过在线文档获取帮助 http://www.github.com https://www.kernel.org/doc/html/latest/ http://httpd.apache.org htt ...