定时事件链表

本文主要写的是:将需要定时的事件作为一个链表节点添加到链表中。所写代码是从LWIP源码中复制出来的,稍作修改。
当阅读到lwip源码timers.c文件中的sys_timeout函数时,觉得非常适合如下一种情况:

step1();
delay(100);
step2()
delay(100);
***
stepn();

上面这段代码是阻塞的,代码运行的时候这种耗费CPU的延时会导致其它代码得不到执行,一般不能写成这种方式。 对于嵌入式开发来说如果有操作系统支持,可以把这一部分分离成一个任务,如果没有操作系统的支持,需要我们自己自己通过状态机或是其它的方法实现,实现的方法有很多种。本文介绍的就是其中的一种解决方案。个人认为这种方案比较优雅。将每个step写成一个事件(函数)。然后挂载到链表上。时间到后执行回调函数。
time_list.h代码如下:

#ifndef __TIME_LIST_H
#define __TIME_LIST_H

typedef unsigned int uint32_t;

typedef void(*time_callback)(void*arg);

typedef struct node
{
	struct node *next;	//用来构成链表
	uint32_t time;	//要延时的时间
	time_callback callback;	//延时时间到后需要指令的回调函数
	void *arg;	//传递给回调函数的参数
}Sys_timeo;

#endif

time_list.c代码如下:

#include 
#include 
#include "time_list.h"
#include "time.h"

#define NULL 0

#define ONE			"ONE"
#define TOW			"TOW"
#define THREE		"THREE"
#define FOUR		"FOUR"

void test_function(void *arg)
{
	time_t t;
	time(&t);
	printf("%u\t", t);
	printf("%s\r\n", arg);
}


Sys_timeo *sys_timeo_list;//定义一个超时链表

/*
	功能:向链表中添加一个定时节点
	msecs:需要定时的时间
	callback:定时时间到后触发的回调函数
	arg:传递给回调函数的参数
	opt:延时选项 0表示相对于当前系统时间延时msecs个时间单位 1表示相对于最后一个节点延时msecs个时间单位
*/
void sys_timeout(uint32_t msecs, time_callback callback, void *arg,uint32_t opt)
{
	Sys_timeo *timeout = NULL,*temp_node = NULL;
	timeout = malloc(sizeof(Sys_timeo));
	if (timeout == NULL)
		return;
	timeout->next = NULL;
	timeout->time = msecs;
	timeout->callback = callback;
	timeout->arg = arg;

	if (sys_timeo_list == NULL)//如果链表是空的,把当前节点作为第一个节点
	{
		sys_timeo_list = timeout;
		return;
	}

	if (opt == 1)
	{
		for (temp_node = sys_timeo_list; temp_node->next != NULL; temp_node = temp_node->next)
		{


		}
		temp_node->next = timeout;
		return;
	}

	/*执行到这里说明链表中存在节点,接下来就找一个合适的位置区添加*/
	if (msecs < sys_timeo_list->time)//先和链表上的头节点对比
	{
		sys_timeo_list->time -= msecs;  //调整时间 因为这个节点已经不再是头节点 调整成相对上一个节点的时间
		timeout->next = sys_timeo_list; //这两行是把节点添加到链表首部
		sys_timeo_list = timeout;
	}
	else
	{
		for (temp_node = sys_timeo_list; temp_node != NULL; temp_node = temp_node->next)
		{
			timeout->time -= temp_node->time; //更新插入节点的时间 减去前边已经遍历过节点的时间
			//插入节点的时间小于当前节点的下一个节点的时间 那么 就找到了位置:  当前节点  要插入的节点  当前节点的下一个节点
			if ((temp_node->next == NULL) || (timeout->time < temp_node->next->time))
			{
				//temp_node->next不是NULL(其实就是timeout插入以后不是最后一个节点) 那么就需要调整后边的那个节点的时间
				if (temp_node->next != NULL)
				{
					temp_node->next->time -= timeout->time;
				}

				timeout->next = temp_node->next;
				temp_node->next = timeout;
				break;
			}
		}
	}
}

int main()
{
	Sys_timeo * temp_node = NULL;
	time_t current_time, last_time, difference_time;

	sys_timeout(10, test_function, ONE, 1);
	sys_timeout(15, test_function, TOW, 1);
	sys_timeout(20, test_function, THREE, 1);
	sys_timeout(25, test_function, FOUR, 1);

	time(&current_time);
	last_time = current_time;

	printf("start_time = %u\r\n",current_time);

	while(1)
	{
		if (sys_timeo_list == NULL)
		{
			printf("exti\r\n");
			return 0;
		}
		time(&current_time);
		difference_time = current_time - last_time;
		if (difference_time)
		{
			last_time = current_time;
			if (sys_timeo_list->time >= difference_time)
				sys_timeo_list->time -= difference_time;
			else
				sys_timeo_list->time = 0;
		}


		if (sys_timeo_list->time == 0)
		{
			sys_timeo_list->callback(sys_timeo_list->arg);
			temp_node = sys_timeo_list;
			sys_timeo_list = sys_timeo_list->next;
			free(temp_node);
		}
	}
	return 0;
}

先看一下Sys_timeo结构体:

typedef struct node
{
	struct node *next;	//用来构成链表
	uint32_t time;	//要延时的时间
	time_callback callback;	//延时时间到后需要指令的回调函数
	void *arg;	//传递给回调函数的参数
}Sys_timeo;

要声明的一点是:结构中time字段延时的时间是相对于上一个节点执行过后还需要延时的时间。
搞清楚这一点后就好办了,我们在中断服务函数中只需要对链表当前的首节点时间做减操作即可。当减到0的时候,执行回调函数,并把链表的首节点释放,下一个节点就是节点了,以此类推。这样就会按既定的时间延时。
函数void sys_timeout(uint32_t msecs, time_callback callback, void *arg,uint32_t opt)会根据需要延时的时间和延时的选项在链表中找到一个合适的位置,将链表节点插入到该位置。介绍一下opt选项,如果为1就是相对于当前链表的尾部节点再延时msecs个时间单位,如果为0就是相对于当前系统时间延时msecs个时间单位。
因为我是在win32环境编写和下测试的,所以就是在主循环中比较当前时间和上次时间的差,以此方法对头节点的时间做减法操作如果在嵌入式中可以在中断服务函数中对链表头部的时间做减法。
在实际使用的时候,在任意时刻需要一个延时时间就调用sys_timeout函数,向sys_timeo_list链表中添加一个延时事件即可。

你可能感兴趣的:(C,链表,数据结构)