引言 -_- 还是老套路开局
很久以前写过一个有追求的线程池 -> C 实现有追求的线程池 探究
讲述的是一种思路, 并且实现了. 可以一用. 最近在详细搞simplec 框架. 准备发布个正式版.
刚好顺带优化一下这个线程池.优化的结果有如下几个方面.
1). 更加美观合理的api
2). pthread线程api 优化
3). 在解决惊群的基础上, 更进一步, 精度定位.
4). 增加了更多的安全性代码
扯淡一点, 线程池对于历史语言C, C++中应用的场景并不多. 可以用线程解决的, 都可以用消息队列解决.
当然线程有个不一样的属性就是可抢占性. 当你追求性能的时候, 那么这个基本满足不了.
至少现代的游戏框架设计中抢占式任务没见有这个必要.
以下容许我分段阐述思路, scthreads.h
#ifndef _H_SIMPLEC_SCTHREADS
#define _H_SIMPLEC_SCTHREADS
#include <schead.h>
//
// 这是个线程池的库. 支持异步取消 也加过一些线程帮助库
//
typedef struct threads * threads_t;
//
// async_run - 开启一个自销毁的线程 运行 run
// run : 运行的主体
// arg : run的参数
// return : >= Success_Base 表示成功
//
extern int async_run(die_f run, void * arg);
//
// threads_create - 创建一个线程池处理对象
// return : 返回创建好的线程池对象, NULL表示失败
//
extern threads_t threads_create(void);
//
// threads_delete - 异步销毁一个线程池对象
// pool : 线程池对象
// return : void
//
extern void threads_delete(threads_t pool);
//
// threads_add - 线程池中添加要处理的任务
// pool : 线程池对象
// run : 运行的执行题
// arg : run的参数
// return : void
//
extern void threads_add(threads_t pool, die_f run, void * arg);
#endif // !_H_SIMPLEC_SCTHREADS
通过上面 可以说明 1). 更加美观合理的api 因为内部使用宏来确定最优线程数. 不需要玩家自己指定.当然这个数值偏小.
前言 -_- 来点开胃点心
有时候我们使用pthread 线程的时候, 步骤有点小繁琐. 我们其实不太需要知道有这个线程, 这个线程执行完毕之后做什么.
只希望简单的帮我异步的执行一个方法就可以了. 这里设计了 thread_run 函数.
typedef void (* die_f)(void * node);
extern int async_run(die_f run, void * arg);
详细的设计套路. 如下
#include <pthread.h>
// 运行的主体
struct func {
die_f run;
void * arg;
};
// thread_run 中 pthread 执行的实体
static void * _run(void * arg) {
struct func * func = arg;
func->run(func->arg);
free(arg);
return NULL;
}
//
// async - 开启一个自销毁的线程 运行 run
// run : 运行的主体
// arg : run的参数
// return : >= Success_Base 表示成功
//
int
async_run(die_f run, void * arg) {
pthread_t tid;
pthread_attr_t attr;
struct func * func = malloc(sizeof(struct func));
if (NULL == func)
RETURN(Error_Alloc, "malloc sizeof(struct func) is error");
func->run = run;
func->arg = arg;
// 构建pthread 线程奔跑起来
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (pthread_create(&tid, &attr, _run, func) < 0) {
free(func);
pthread_attr_destroy(&attr);
RETURN(Error_Base, "pthread_create error run, arg = %p | %p.", run, arg);
}
pthread_attr_destroy(&attr);
return Success_Base;
}
这里扯一点, 第一个是我常用的通用错误枚举.
//
// flag_e - 全局操作基本行为返回的枚举, 用于判断返回值状态的状态码
// >= 0 标识 Success状态, < 0 标识 Error状态
//
typedef enum {
Success_Exist = +2, //希望存在,设置之前已经存在了.
Success_Close = +1, //文件描述符读取关闭, 读取完毕也会返回这个
Success_Base = +0, //结果正确的返回宏
Error_Base = -1, //错误基类型, 所有错误都可用它, 在不清楚的情况下
Error_Param = -2, //调用的参数错误
Error_Alloc = -3, //内存分配错误
Error_Fd = -4, //文件打开失败
Error_TOUT = -5, //超时错误
} flag_e;
项目实战中运用的很好. 基本一个函数返回的错误就那些.
再扯第二点. 在我们使用 pthread_attr_init的时候posix线程推荐我们立即也必须调用 pthread_attr_destroy.
保证你自己的东西自己释放. 实际上 pthread_*_destroy 这类函数只是返回当前线程状态, 不涉及资源销毁内容.
再扯第三点, 好用的RETURN宏, 还是挺飘的.
//
// 控制台输出完整的消息提示信息, 其中fmt必须是 "" 包裹的字符串
// CERR -> 简单的消息打印
// CERR_EXIT -> 输出错误信息, 并推出当前进程
// CERR_IF -> if语句检查, 如果符合标准错误直接退出
//
#ifndef _H_CERR
#define _H_CERR
#define CERR(fmt, ...) \
fprintf(stderr, "[%s:%s:%d][errno %