1、定时器的应用
心跳检测
判断一个服务是否挂掉,例如在服务端和客户端之间的通信过程中,服务端会定时的向客户端发送一个心跳包,用于检测连接是否正常技能冷却
武器冷却
倒计时
其它需要使⽤超时机制的功能
对于服务端来说,驱动服务端逻辑的事件主要有两个,⼀个是⽹络事件,另⼀个是时间事件
在不同框架中,这两种事件有不同的实现⽅式:
第⼀种,⽹络事件和时间事件在⼀个线程当中配合使⽤;例如nginx、redis
while (!quit) {
int now = get_now_time();// 单位:ms
int timeout = get_nearest_timer() - now;
if (timeout < 0) timeout = 0;
int nevent = epoll_wait(epfd, ev, nev, timeout);
for (int i=0; i<nevent; i++) {
//... ⽹络事件处理
}
update_timer(); // 时间事件处理
}
一般的,我们会拿最近要触发的定时任务的时间,用这个时间减去当前时间,作为epoll_wait等待网络时间的最后一个参数,也就是timeout。如果在timeout这个时间段里,没有网络事件发生,epoll_wait就会返回0,网络事件也就不会被处理。如果在timeout这个时间段里,有网络事件发生,网络事件就会被处理。不过无论是前者还是后者,最终都会处理定时任务。
第⼆种,⽹络事件和时间事件在不同线程当中处理;例如skynet
// 第⼆种 在其他线程添加定时任务
void* thread_timer(void * thread_param) {
init_timer();
while (!quit) {
update_timer(); // 更新检测定时器,并把定时事件发送到消息队列中
sleep(t); // 这⾥的 t 要⼩于 时间精度
}
clear_timer();
return NULL;
}
pthread_create(&pid, NULL, thread_timer, &thread_param);
这里有消息队列,网络线程,定时器线程,工作线程池。
网路线程和定时器线程将事件放在消息队列里,由工作线程池去消耗。
网络线程和工作线程池如果有定时任务处理,则会交给定时器线程,定时器线程再其放在消息队列中
2、定时器的设计
接口设计
// 初始化定时器
void init_timer();
// 添加定时器
Node* add_timer(int expire, callback cb);//发生时间和回调指针
// 删除定时器
bool del_timer(Node* node);
// 找到最近要发⽣的定时任务
Node* find_nearest_timer();
// 更新检测定时器
void update_timer();
// 清除定时器
// void clear_timer();
数据结构选择
红黑树
对于增删查,时间复杂度为O(
l
o
g
2
N
log_2N
log2N)。对于红⿊树最⼩节点为最左侧节点,时间复杂度为O(
l
o
g
2
N
log_2N
log2N)
最小堆
对于增查,时间复杂度为O(
l
o
g
2
N
log_2N
log2N)。对于删时间复杂度为O(n),但是可以通过辅助数据结构(map或者hashtable来快速索引节点)来加快删除操作;对于最小节点为根节点,时间复杂度为O(1)
跳表
对于增删查,时间复杂度为O(
l
o
g
2
N
log_2N
log2N)。对于跳表最⼩节点为最左侧节点,时间复杂度为O(1),但是空间复杂度比较⾼,为O(1.5n)
时间轮
对于增删查,时间复杂度为O(1)。查找最⼩节点也为O(1)
3、最小堆实现
整体实现:
minheap.h
#pragma once
#include<vector>
#include<map>
using namespace std;
typedef void (*TimerHandler) (struct TimerNode * node);
struct TimerNode{
int idx = 0;//定时器在vector中所处的下标
int id = 0;//定时器id
unsigned int expire = 0;//处理事件
TimerHandler cb = NULL;//回调指针
};
class MinHeapTimer {
public:
MinHeapTimer() {
_heap.clear();
_map.clear();
}
//记录总共定时器的数量,包括已经删除了的定时器,该参数作为定时器的id
static inline int Count() {
return ++_count;
}
//添加任务,expire为定时时间,到时间后就要执行cb回调函数
int AddTimer(uint32_t expire, TimerHandler cb);
//通过id删除任务,不过里面需要调用_delNode进行删除
bool DelTimer(int id);
//执行任务
void ExpireTimer();
private:
//比较任务定时时间
inline bool _lessThan(int lhs, int rhs) {
return _heap[lhs]->expire < _heap[rhs]->expire;
}
//向下调整
void _shiftDown(int pos);
//向上调整
void _shiftUp(int pos);
//
void _delNode(TimerNode *node);
private:
vector<TimerNode*> _heap;//存放定时器
map<int, TimerNode*> _map;//定时器id和定时器的映射,提高查找效率
static int _count;
};
int MinHeapTimer::_count = 0;
minheap.cpp
#include <sys/time.h>
#include <unistd.h>
#include <time.h>
#include <iostream>
#include "minheap.h"
//获取系统时间,单位是毫秒
static uint32_t current_time()
{
// struct timespec
// {
// time_t tv_sec; /* 秒*/
// long tv_nsec; /* 纳秒*/
// };
uint32_t t;
struct timespec ti;
// CLOCK_MONOTONIC参数表示从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
clock_gettime(CLOCK_MONOTONIC, &ti);
t = (uint32_t)ti.tv_sec * 1000;
t += ti.tv_nsec / 1000000;
return t;
}
int MinHeapTimer::AddTimer(uint32_t expire, TimerHandler cb)
{
int64_t timeout = current_time() + expire;
TimerNode *node = new TimerNode;
int id = Count();
node->id = id;
node->expire = timeout;
node->cb = cb;
node->idx = (int)_heap.size();
_heap.push_back(node);
_shiftUp((int)_heap.size() - 1);
_map.insert(make_pair(id, node));
return id;
}
void MinHeapTimer::_shiftUp(int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (_lessThan(child, parent))
{
std::swap(_heap[child], _heap[parent]);
_heap[child]->idx = child;
_heap[parent]->idx = parent;
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
bool MinHeapTimer::DelTimer(int id)
{
auto it = _map.find(id);
if (it == _map.end())
{
return false;
}
_delNode(it->second);
}
void MinHeapTimer::_delNode(TimerNode *node)
{
int last = (int)_heap.size() - 1;
int idx = node->idx;
if (last != idx)
{
//交换位置,并更改idx
std::swap(_heap[last], _heap[idx]);
_heap[idx]->idx = idx;
_shiftDown(idx);
}
//从vector中删除删除最后一个定时器(node)
_heap.pop_back();
//在map中取消定时id和定时器的映射关系
_map.erase(node->id);
delete node;
}
void MinHeapTimer::_shiftDown(int parent)
{
int size = _heap.size();
int child = parent * 2 + 1; //默认左小于右
while (child < size - 1)
{
if (child + 1 < size - 1 && !_lessThan(child, child + 1))
{
child++;
}
if (!_lessThan(parent, child))
{
std::swap(_heap[parent], _heap[child]);
_heap[parent]->idx = parent;
_heap[child]->idx = child;
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void MinHeapTimer::ExpireTimer()
{
//没有任务
if (_heap.empty())
{
return;
}
uint32_t now = current_time(); //获取当前时间
while (!_heap.empty())
{
//获取最近任务,堆顶,其他任务的时间都比堆顶的时间大
TimerNode *node = _heap.front();
//最近任务的时间比当前时间大,说明时间还没到,不执行,其他任务也不执行
if (node->expire > now)
{
break;
}
for (int i = 0; i < _heap.size(); i++)
std::cout << "touch idx: " << _heap[i]->idx
<< " id: " << _heap[i]->id << " expire: "
<< _heap[i]->expire << std::endl;
if (node->cb)
{
node->cb(node);
}
_delNode(node);
}
}
void print_hello(TimerNode *te) {
std::cout << "hello world time = " << te->idx << "\t" << te->id << std::endl;
}
int main() {
MinHeapTimer mht;
mht.AddTimer(0, print_hello);
mht.AddTimer(1000, print_hello);
mht.AddTimer(7000, print_hello);
mht.AddTimer(2000, print_hello);
mht.AddTimer(9000, print_hello);
mht.AddTimer(10000, print_hello);
mht.AddTimer(6000, print_hello);
mht.AddTimer(3000, print_hello);
for (;;) {
mht.ExpireTimer();
usleep(10000);
}
return 0;
}
测试:
4、时间轮实现
Linux内核中的定时器就是用时间轮来实现的
单层级时间轮
心跳检测:
客户端每 5 秒钟发送心跳包;服务端若 10 秒内没收到心跳数据,则清除连接;
实际在开发过程中,若收到除了心跳包的其他数据,心跳检测也算通过,在这里为了简化流程,只判断心跳包;
作为对比:我们假设使用map<int, conn*> 来存储所有连接数;每秒检测 map 结构,那么每秒需要遍历所有的连接,如果这个map结构包含级几万条连接,那么我们做了很多无效检测;考虑极端情况,刚添加进来的连接,下⼀秒就需要去检测,实际上只需要10秒后检测就⾏了;那么我们考虑使用时间轮来检测;
注意:这个例子只是用来帮助理解时间轮,不代表实际解决放案;
设计:
整体实现:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#define MAX_TIMER ((1<<17)-1)
//最大的链接个数,linux默认是65535,但可以更改
#define MAX_CONN ((1<<16)-1)
//连接的节点,用来记录心跳包发送的次数
typedef struct conn_node {
//引用计数的次数,当used==0,相当于自动销毁
uint8_t used;
int id;
} conn_node_t;
//定时器
typedef struct timer_node {
struct timer_node *next;
struct conn_node *node;
uint32_t idx;
} timer_node_t;
//用数组,最开始就初始化好,就不用new,delete,从而提升效率
static timer_node_t timer_nodes[MAX_TIMER] = {0};
static conn_node_t conn_nodes[MAX_CONN] = {0};
static uint32_t t_iter = 0;
static uint32_t c_iter = 0;
//获取一个没有被使用的timer_node_t
timer_node_t * get_timer_node() { // 注意:没有检测定时任务数超过 MAX_TIMER 的情况
t_iter++;
while (timer_nodes[t_iter & MAX_TIMER].idx > 0) {
t_iter++;
}
timer_nodes[t_iter].idx = t_iter;
return &timer_nodes[t_iter];
}
//获取一个没有被使用的conn_node_t
conn_node_t * get_conn_node() { // 注意:没有检测连接数超过 MAX_CONN 的情况
c_iter++;
while (conn_nodes[c_iter & MAX_CONN].used > 0) {
c_iter++;
}
return &conn_nodes[c_iter];
}
//哈希桶的个数
#define TW_SIZE 16
//检测心跳包的延迟时间,由于哈希桶的个数有限,所以心跳包发送时间不能够超过6
#define EXPIRE 10
#define TW_MASK (TW_SIZE - 1)
//当前所处的时间
static uint32_t tick = 0;
typedef struct link_list {
timer_node_t head;
//tail指针能提高插入效率
timer_node_t *tail;
}link_list_t;
//
void add_conn(link_list_t *tw, conn_node_t *cnode, int delay) {
//(tick+EXPIRE+delay) & TW_MASK 等价于 (tick+EXPIRE+delay)%TW_SIZE 前者只是为了提高效率
link_list_t *list = &tw[(tick+EXPIRE+delay) & TW_MASK];
timer_node_t * tnode = get_timer_node();
cnode->used++;
//将定时器链接到对应的哈希桶下
tnode->node = cnode;
list->tail->next = tnode;
list->tail = tnode;
tnode->next = NULL;
}
//清除哈希桶或者初始化
void link_clear(link_list_t *list) {
list->head.next = NULL;
list->tail = &(list->head);
}
//检测当前时间下是否用定时任务,如果有就触发
void check_conn(link_list_t *tw) {
int32_t itick = tick;
tick++;
link_list_t *list = &tw[itick & TW_MASK];
timer_node_t *current = list->head.next;
while (current) {
timer_node_t * temp = current;
current = current->next;
conn_node_t *cn = temp->node;
//每检测一次,对应的心跳包次数就减少1
cn->used--;
//将该定时器的idx置为0,表示该定时器已经作废
temp->idx = 0;
//如果used == 0,说明在前10秒之内,没有发送心跳包,需断开连接
if (cn->used == 0) {
printf("链接fd:%d 已断开\n", cn->id);
temp->next = NULL;
continue;
}
printf("链接fd:%d 还有%d个心跳包需要检测\n", cn->id, cn->used);
}
//当前时间下对应的心跳包都检测完,需要清除哈希桶
link_clear(list);
}
//获取当前的时间,单位为s
static time_t current_time()
{
time_t t;
struct timespec ti;
clock_gettime(CLOCK_MONOTONIC, &ti);
t = (time_t)ti.tv_sec;
return t;
}
int main()
{
memset(timer_nodes, 0, MAX_TIMER * sizeof(timer_node_t));
memset(conn_nodes, 0, MAX_CONN * sizeof(conn_node_t));
// init link list
link_list_t tw[TW_SIZE];
memset(tw, 0, TW_SIZE * sizeof(link_list_t));
for (int i = 0; i < TW_SIZE; i++) {
link_clear(&tw[i]);
}
// 该测试起始时间为0秒,所以 delay 不能添加超过6的数。
// 第一个连接对应的心跳包,在0和5时进行发送
//所以会在10s和15s时进行检测,15s时把该连接断开
{
conn_node_t *node = get_conn_node();
node->id = 10001;
add_conn(tw, node, 0);
add_conn(tw, node, 5);
}
//第二个连接发送的心跳包,在第10s时进行检测,然后断开
{
conn_node_t *node = get_conn_node();
node->id = 10002;
add_conn(tw, node, 5);
}
//第三个连接发送的心跳包,在第10s时检测,然后断开
{
conn_node_t *node = get_conn_node();
node->id = 10003;
add_conn(tw, node, 0);
}
time_t start = current_time();
for (;;) {
time_t now = current_time();
if (now - start > 0) {
for (int i=0; i<now-start; i++)
check_conn(tw);
start = now;
printf("在第%d秒时检测\n", tick);
}
}
return 0;
}
测试:
因为哈希桶大小为16,并且需要我们延迟10秒检测心跳包,所以发送心跳包的时间只能是[0,5]秒。换句话说,想要扩大心跳包发送时间的范围,只能扩大哈希桶的大小
多层级时间轮
时钟表盘:每过1秒钟,秒针就移动一格,秒钟移动60格,分针移动1格,分针移动60格,时针移动1格。
参照时钟表盘的运转规律,可以将定时任务根据触发的紧急程度,分布到不同层级的时间轮中;
假设时间精度为 10ms ;在第 1 层级每 10ms 移动⼀格;每移动⼀格执行该格⼦当中所有的定时任务;
当第 1 层指针从 255 格开始移动,此时层级 2 移动⼀格;层级 2 移动⼀格的行为定义为,将该格当中的定时任务重新映射到层级 1 当中;同理,层级 2 当中从 63 格开始移动,层级 3 格子中的定时任务重新映射到层级 2 ; 以此类推层级 4 往层级 3 映射,层级 5 往层级 4 映射;
如何重新映射?定时任务的过期时间对上⼀层级的长度取余分布在上⼀层级不同格子当中;
2560ms(2.56s)定时任务定义为紧急任务
这个多层级时间轮最大可以支持4,294,967,296ms的定时任务(25664646464)
下面的例子能很好帮我们理解多层级时间轮的运行过程:
现在有一个2580ms的定时任务,因为时间超过了第一层的最大时间2560ms,所以需要将这个定时任务放在第二层的0位置处,当第一层的指针移动了一圈后(2560ms)后,定时任务就从第二层回退到第一层,这个定时任务还剩20ms的时间,所以就将它映射到第一层的1位置处。
如果定时任务是5140ms,则在第二层的1位置处,第一层指针移动一圈,定时任务回退到第二层0位置处,第一层指针再移动一圈,定时任务才回退到第一层的1位置处。
以此类推
所以只有层级为1的指针才会执行对应的定时任务
整体实现:
spinlock.h
#ifndef SPINLOCK_H
#define SPINLOCK_H
struct spinlock
{
int lock;
};
void spinlock_init(struct spinlock *lock)
{
lock->lock = 0;
}
void spinlock_lock(struct spinlock *lock) {
while (__sync_lock_test_and_set(&lock->lock, 1)) {}
}
int spinlock_trylock(struct spinlock *lock) {
return __sync_lock_test_and_set(&lock->lock, 1) == 0;
}
void spinlock_unlock(struct spinlock *lock) {
__sync_lock_release(&lock->lock);
}
void spinlock_destroy(struct spinlock *lock) {
(void) lock;
}
#endif
timewheel.h
#ifndef _MARK_TIMEWHEEL_
#define _MARK_TIMEWHEEL_
#include <stdint.h>
#define TIME_NEAR_SHIFT 8
#define TIME_NEAR (1 << TIME_NEAR_SHIFT) //256
#define TIME_LEVEL_SHIFT 6
#define TIME_LEVEL (1 << TIME_LEVEL_SHIFT)//64
#define TIME_NEAR_MASK (TIME_NEAR-1)//255
#define TIME_LEVEL_MASK (TIME_LEVEL-1)//63
typedef struct timer_node timer_node_t;
typedef void (*handler_pt) (struct timer_node *node);
struct timer_node {
struct timer_node *next;
uint32_t expire;
handler_pt callback;
uint8_t cancel;
int id; // 此时携带参数
};
timer_node_t* add_timer(int time, handler_pt func, int threadid);
void expire_timer(void);
void del_timer(timer_node_t* node);
void init_timer(void);
void clear_timer();
#endif
timewheel.c
#include "spinlock.h"
#include "timewheel.h"
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <time.h>
typedef struct link_list
{
timer_node_t head;
timer_node_t *tail;
} link_list_t;
//多级时间轮
typedef struct timer
{
//第一级
link_list_t near[TIME_NEAR];
// 2-4级
link_list_t t[4][TIME_LEVEL];
struct spinlock lock;
uint32_t time;
uint64_t current;
uint64_t current_point;
} s_timer_t;
static s_timer_t *TI = NULL;
timer_node_t *link_clear(link_list_t *list)
{
timer_node_t *ret = list->head.next;
list->head.next = 0;
list->tail = &(list->head);
return ret;
}
//链接一个节点
void Link(link_list_t *list, timer_node_t *node)
{
list->tail->next = node;
list->tail = node;
node->next = 0;
}
void add_node(s_timer_t *T, timer_node_t *node)
{
uint32_t time = node->expire;
uint32_t current_time = T->time;
uint32_t msec = time - current_time;
//根据时间
if (msec < TIME_NEAR)
{ //[0, 0x100)
Link(&T->near[time & TIME_NEAR_MASK], node);
}
else if (msec < (1 << (TIME_NEAR_SHIFT + TIME_LEVEL_SHIFT)))
{ //[0x100, 0x4000)
Link(&T->t[0][((time >> TIME_NEAR_SHIFT) & TIME_LEVEL_MASK)], node);
}
else if (msec < (1 << (TIME_NEAR_SHIFT + 2 * TIME_LEVEL_SHIFT)))
{ //[0x4000, 0x100000)
Link(&T->t[1][((time >> (TIME_NEAR_SHIFT + TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)], node);
}
else if (msec < (1 << (TIME_NEAR_SHIFT + 3 * TIME_LEVEL_SHIFT)))
{ //[0x100000, 0x4000000)
Link(&T->t[2][((time >> (TIME_NEAR_SHIFT + 2 * TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)], node);
}
else
{ //[0x4000000, 0xffffffff]
Link(&T->t[3][((time >> (TIME_NEAR_SHIFT + 3 * TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)], node);
}
}
//增加事件
timer_node_t *add_timer(int time, handler_pt func, int threadid)
{
timer_node_t *node = (timer_node_t *)malloc(sizeof(*node));
spinlock_lock(&TI->lock);
node->expire = time + TI->time; // 每10ms加1 0
node->callback = func;
node->id = threadid;
if (time <= 0)
{
node->callback(node);
free(node);
spinlock_unlock(&TI->lock);
return NULL;
}
add_node(TI, node);
spinlock_unlock(&TI->lock);
return node;
}
void move_list(s_timer_t *T, int level, int idx)
{
timer_node_t *current = link_clear(&T->t[level][idx]);
while (current)
{
timer_node_t *temp = current->next;
add_node(T, current);
current = temp;
}
}
void timer_shift(s_timer_t *T)
{
int mask = TIME_NEAR;
uint32_t ct = ++T->time;
if (ct == 0)
{
move_list(T, 3, 0);
}
else
{
// ct / 256
uint32_t time = ct >> TIME_NEAR_SHIFT;
int i = 0;
// ct % 256 == 0
while ((ct & (mask - 1)) == 0)
{
int idx = time & TIME_LEVEL_MASK;
if (idx != 0)
{
move_list(T, i, idx);
break;
}
mask <<= TIME_LEVEL_SHIFT;
time >>= TIME_LEVEL_SHIFT;
++i;
}
}
}
void dispatch_list(timer_node_t *current)
{
do
{
timer_node_t *temp = current;
current = current->next;
if (temp->cancel == 0)
temp->callback(temp);
free(temp);
} while (current);
}
void timer_execute(s_timer_t *T)
{
int idx = T->time & TIME_NEAR_MASK;
while (T->near[idx].head.next)
{
timer_node_t *current = link_clear(&T->near[idx]);
spinlock_unlock(&T->lock);
dispatch_list(current);
spinlock_lock(&T->lock);
}
}
void timer_update(s_timer_t *T)
{
spinlock_lock(&T->lock);
timer_execute(T);
timer_shift(T);
timer_execute(T);
spinlock_unlock(&T->lock);
}
void del_timer(timer_node_t *node)
{
node->cancel = 1;
}
s_timer_t *timer_create_timer()
{
s_timer_t *r = (s_timer_t *)malloc(sizeof(s_timer_t));
memset(r, 0, sizeof(*r));
int i, j;
for (i = 0; i < TIME_NEAR; i++)
{
link_clear(&r->near[i]);
}
for (i = 0; i < 4; i++)
{
for (j = 0; j < TIME_LEVEL; j++)
{
link_clear(&r->t[i][j]);
}
}
spinlock_init(&r->lock);
r->current = 0;
return r;
}
uint64_t gettime()
{
uint64_t t;
struct timespec ti;
clock_gettime(CLOCK_MONOTONIC, &ti);
t = (uint64_t)ti.tv_sec * 100;
t += ti.tv_nsec / 10000000;
return t;
}
void expire_timer(void)
{
uint64_t cp = gettime();
if (cp != TI->current_point)
{
uint32_t diff = (uint32_t)(cp - TI->current_point);
TI->current_point = cp;
int i;
for (i = 0; i < diff; i++)
{
timer_update(TI);
}
}
}
void init_timer(void)
{
TI = timer_create_timer();
TI->current_point = gettime();
}
void clear_timer()
{
int i, j;
for (i = 0; i < TIME_NEAR; i++)
{
link_list_t *list = &TI->near[i];
timer_node_t *current = list->head.next;
while (current)
{
timer_node_t *temp = current;
current = current->next;
free(temp);
}
link_clear(&TI->near[i]);
}
for (i = 0; i < 4; i++)
{
for (j = 0; j < TIME_LEVEL; j++)
{
link_list_t *list = &TI->t[i][j];
timer_node_t *current = list->head.next;
while (current)
{
timer_node_t *temp = current;
current = current->next;
free(temp);
}
link_clear(&TI->t[i][j]);
}
}
}
tw-timer.c
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <stdlib.h>
#include "timewheel.h"
struct context {
int quit;
int thread;
};
struct thread_param {
struct context *ctx;
int id;
};
static struct context ctx = {0};
void do_timer(timer_node_t *node) {
printf("timer expired:%d - thread-id:%d\n", node->expire, node->id);
}
void* thread_worker(void *p) {
struct thread_param *tp = p;
int id = tp->id;
struct context *ctx = tp->ctx;
while (!ctx->quit) {
int expire = rand() % 200;
add_timer(expire, do_timer, id);
usleep(expire*(10-1)*1000);
}
printf("thread_worker:%d exit!\n", id);
return NULL;
}
void do_quit(timer_node_t * node) {
ctx.quit = 1;
}
int main() {
srand(time(NULL));
//8个线程
ctx.thread = 8;
pthread_t pid[ctx.thread];
init_timer();
add_timer(6000, do_quit, 100);
struct thread_param task_thread_p[ctx.thread];
int i;
for (i = 0; i < ctx.thread; i++) {
task_thread_p[i].id = i;
task_thread_p[i].ctx = &ctx;
if (pthread_create(&pid[i], NULL, thread_worker, &task_thread_p[i])) {
fprintf(stderr, "create thread failed\n");
exit(1);
}
}
while (!ctx.quit) {
expire_timer();
usleep(2500);
}
clear_timer();
for (i = 0; i < ctx.thread; i++) {
pthread_join(pid[i], NULL);
}
printf("all thread is closed\n");
return 0;
}
测试: