C 实现有追的线程池 后续

引言  -_-  仍旧老套路开局

  很久从前写过一个出追求的线程池 -> 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 %d:%s]" fmt "\n",\
        __FILE__, __func__, __LINE__, errno, strerror(errno), ##__VA_ARGS__)

#define CERR_EXIT(fmt,...) \
    CERR(fmt, ##__VA_ARGS__), exit(EXIT_FAILURE)

#define CERR_IF(code) \
    if((code) < 0) \
        CERR_EXIT(#code)

//
// RETURN - 打印错误信息, 并return 返回指定结果
// val        : return的东西, 当需要 return void; 时候填 ',' 就过 or NIL
// fmt        : 双引号包裹的格式化字符串
// ...        : fmt中对应的参数
// return     : val
// 
#define NIL
#define RETURN(val, fmt, ...) \
    do {\
        CERR(fmt, ##__VA_ARGS__);\
        return val;\
    } while(0)


#endif

##  是以缓解,
可转换参数中然而来一个参数的题目(… 为 empty 没有内容, GCC编译器不过).

NIL 是以化解 return void; 语法 被 RETURN(NIL) 这种语法糖替代.

归来正题, 上边函数其实就是展现了 2). pthread线程api 优化 . 首要反映在
我所以 

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

来替代
  pthread_detach(pthread_self());

把线程启动运行的装, 移动到线程外边起初化中. 意思就是是创即刻拥有.

跌落了线程控制代码的耦合性, 微量为线程业务代码提速了.

启航慢, 运行快.  或者说不识的时光难认识, 精通后好相处其实还好.
O(∩_∩)O哈哈~

 

正文  -_-  详细的统筹

  首先看主题结构, 每个线程对象

// 线程结构体, 每个线程一个信号量, 定点触发
struct thread {
    struct thread * next;        // 下一个线程对象
    bool wait;                   // true 表示当前线程被挂起
    pthread_t tid;               // 当前线程id
    pthread_cond_t cond;         // 线程条件变量
};

线程启动对象是一个链表. wait代表手上线程挂于状态,
用于可以高效激活挂于底线程.

// 找到空闲的线程, 并返回起信号量 
static pthread_cond_t * _threads_getcont(struct threads * pool) {
    struct thread * head = pool->thrs;
    while (head) {
        if (head->wait)
            return &head->cond;
        head = head->next;
    }
    return NULL;
}

里 struct threads 是具线程对象的调度结构.

// 定义线程池(线程集)定义
struct threads {
    size_t size;                // 线程池大小, 最大线程结构体数量
    size_t curr;                // 当前线程池中总的线程数
    size_t idle;                // 当前线程池中空闲的线程数
    pthread_mutex_t mutx;       // 线程互斥量
    struct thread * thrs;       // 线程结构体对象集
    struct job * head;          // 线程任务链表的链头, 队列结构
    struct job * tail;          // 线程任务队列的表尾, 后插入后执行
};

职责job采取的是一个列结构.  线程链表同时消耗是生产者队列.

地点wait 设计显示了 3). 在化解惊群的底子及, 更进一步, 精度定位.

 

对于第四沾 4). 增添了再也多之安全性代码 大家的做法显示于pthread
线程的性决定上.

    // 设置线程属性, 设置线程 允许退出线程
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

装开启线程撤消状态, 并且匡助异步撤除.

    // 构建线程, 构建完毕直接获取
    if (pool->idle > 0) {
        pthread_cond_t * cond = _threads_getcont(pool);
        // 先释放锁后发送信号激活线程, 速度快, 缺点丧失线程执行优先级
        pthread_mutex_unlock(mutx);
        // 发送给空闲的线程, 这个信号量一定存在
        pthread_cond_signal(cond);
        return;
    }

固化发送信号, 精准的化解了惊群现象. 会用空间更换时间这就更换,
可是毫无浪费.

闲谈一点任何惊群, 例如以差不多进程被epoll中.  fork 后 epoll -> accept
只出一个中标,两只败北.

缓解方案吗是有的.最简易即是忽视惊群错误,
不过性有硌影响.也堪透过平衡轮询文件讲述符处理.

对于本线程池相关的详尽表达, 可以关押下几乎独自文件及测试文件

  scthreads.h

  scthreads.c

  [test_scthreads.c](https://github.com/wangzhione/simplec/blob/master/simplec/test/test_scthreads.c)

商最后, 改动的第一缘由是先那么本子太讨厌了, 看无惯. 觉得美是好的,
美是一模一样栽愉悦的感想~ _φ( °-°)/

为美怎么收拾, 这虽然整呗~

 

后记  -_-  多留下点记忆吧, 说不定就忘了

C++,  问题是难免的, 唯有打磨探讨~

  北方之故事  http://music.163.com/\#/song?id=37782112

  C++ 1

  君羡慕我的心无旁骛, 我羡慕你的幸福生活