设计一条简单的等待工作队列之软件模型设计与实现(二)

简介: 上节实现了一条最简单的线程等待工作队列。http://blog.csdn.net/morixinguan/article/details/77758206但设计还有诸多因素需要考虑和改进,例如以下:void print(queue_list *header){ int count =...

上节实现了一条最简单的线程等待工作队列。

http://blog.csdn.net/morixinguan/article/details/77758206

但设计还有诸多因素需要考虑和改进,例如以下:

void print(queue_list *header)
{
	int count = 0 ;
	while(1)
	{
		sleep(1);
		printf("工作队列 work_serial_num: %d   hello kitty.....!\n",header->work_queue.work_serial_num);
		count++ ;
		if(count == 10)
			break ;
	}
}
在上面一篇文章的代码中,其中一个工作任务出现了sleep的延迟操作,那么一旦工作任务一多,是不是每个工作队列都需要去调用一个sleep函数?那显得很浪费。。。

我们不如在工作结构体中嵌套两个元素,分别是工作延迟的时间设定,以及最终调用延迟func实现延迟,就像设计等待倒计时一样,时间一到,就出队。这个结构体改进如下:

//工作者结构体
typedef struct __work_st
{
	//工作者编号 
	int work_serial_num;
	//延迟的时间,以s来计算 
	int sec ;
	//工作产生的时间延迟(使用sleep函数产生) 
	void (*work_sleep)();
	//工作者执行操作 
	void (*queue_st)(); 
}work_st ; 
这样,在外头实现对sleep函数的封装,可以实现如下:

//每一个工作所对应的时间 
void work_sleep(queue_list *header)
{
	printf("工作线程等待%ds后执行\n",header->work_queue.sec);
	sleep(header->work_queue.sec);
}
sec和work_sleep在入队时对当前的节点进行初始化,如果有多个工作任务插入,则往后指向下一个节点,依次类推。这样出队的时候就调用初始化好的每个节点对应的数据,执行等待,后面再执行对应的任务。

在出队的时候设计原来是这样的结构:

while(NULL != p->next)
{
    p = p->next ;
}
这样在出队的时候就会将入队的所有数据全部出队,这样做不是不对。但如果考虑一个问题,要是一次只出一个,那这个接口就不符合设计要求了。所以,我们改进这个接口,让它调用一次的时候,通过判断队列头来出队,一次只出队一个任务,而不是全部出队,当然也可以全部出队,取决于程序逻辑的实现。

改进接口如下:

//逐个出队 
//出队 
queue_list *De_queue(queue_list *header)
{
	int ret ;
	int count = 0;
	int queue_length = 0;
	pthread_t tid ;
	queue_list *p = header ;
	queue_list *prev  = NULL ;
	//如果当前节点的下一个节点为空,则返回空 
	if(header->next == NULL){
		free(header);
		header = NULL ;
		printf("header mem:0x%p\n",header);
		return header ;
	}
	prev = p ;
	p = p->next ;
	ret = pthread_create(&tid, NULL , (void *)p->work_queue.work_sleep, p);
	if(ret < 0){
		printf("create work_thread fair!ssssssssssssssssssssssssss\n");
		return ;
	}
	//等待对应的工作结束 
	pthread_join(tid,NULL);
	
	//每出队一次,就相当于创建一条线程来执行对应的工作 
	ret = pthread_create(&tid, NULL , (void *)p->work_queue.queue_st, p);
	if(ret < 0){
		printf("create work_thread fair!ssssssssssssssssssssssssss\n");
		return ;
	}
	//等待对应的工作结束 
	pthread_join(tid,NULL);
	//获取队列的长度 
	queue_length = Get_queue_Length(header);
	queue_length = queue_length - 1;
	header->queue_length = queue_length ;
	printf("当前队列的长度:%d  %d\n",queue_length,header->queue_length);
	prev->next = p->next ;
	free(p);
	return prev ;
}
这里设计还要重视一个比较关键的问题,这也是所有初学者不会考虑到的问题,就是内存的分配和释放的问题。

如上程序,如果free(header)后不将header指向NULL,那么header此时是个野指针。那么返回后,在主程序中判断header的时候,它一定不为空。这种问题,想必大学老师在讲课的时候几乎都没有去讲过,所以很多人一直被误导,malloc到的内存,free掉以后指针就为空了,是这样吗?我们可以写个程序测测看:

#include <stdio.h>
#include <stdlib.h>
#define SIZE 10

int main(void)
{
	int *p = NULL ; 
	p = malloc(SIZE) ;
	*p = 100 ;
	printf("malloc size: %d    malloc mem:0x%p  %d\n",SIZE,p,*p);
	free(p);
	printf("malloc size: %d    malloc mem:0x%p\n",SIZE,p);
	//p = NULL ;
	printf("malloc size: %d    malloc mem:0x%p   %d\n",SIZE,p,*p);
	return 0 ;
}
运行结果:


看到了吧,mallco后,对p指针简引用赋值,得到的是100,而free后,虽然释放了内存,但简引用指针p的时候却是个随机值,此时它已经成为了一个野指针。

如果我们把p = NULL加上呢?什么样的结果:

运行结果:


很明显,对一个p = NULL的指针简引用,它一定会段错误。

扯多了,接下来看看等待工作队列的改进整体的实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<pthread.h>
#include <Windows.h>
#define QUEUE_LEN  10 


//工作者队列实现 
typedef struct __work_st
{
	//工作者编号 
	int work_serial_num;
	//延迟的时间,以s来计算 
	int sec ;
	//工作产生的时间延迟(使用sleep函数产生) 
	void (*work_sleep)();
	//工作者执行操作 
	void (*queue_st)(); 
}work_st ; 


//队列结构体实现 
typedef struct queue_st
{
	work_st work_queue ;
	//队列的最大长度
	int queue_max_length ; 
	//队列的实际长度
	int queue_length ; 
	//队列是否为空的标志
	int Null_flag ;
	//队列是否为满的标志
	int Full_flag ; 
	//指向下一个队列节点 
	struct queue_st *next ;
}queue_list;

//初始化队列头---->虚设,没有意义,只是为了方便操作 
void Init_queue_header(queue_list *header , int queue_length) ; 
//检查当前队列是否已经满了 ,通过队列满标志和队列最大长度这两个条件同时成立来判断 
int Check_queue_full(queue_list *header , int queue_length);
//入队--->将要执行的编号和功能函数注册到队列中去 
int En_queue(queue_list *header , work_st *workArray) ;
//出队--->每个编号对应的功能函数会对应创建一条线程,等待第一条工作队列结束后,才会执行接下来的
//工作。 
queue_list *De_queue(queue_list *header) ;
//检查当前的队列是否为空,检查的标准是不包括队列头,通过引用计数来计算队列的长度 
int Check_queue_null(queue_list *header) ;
//获取当前队列的长度,不包括队列头 
int Get_queue_Length(queue_list *header) ;
//每一个工作所对应的时间 
void work_sleep(queue_list *header);


void print(queue_list *header)
{
	int count = 0 ;
	while(1)
	{
		printf("工作队列 work_serial_num: %d   hello kitty.....!\n",header->work_queue.work_serial_num);
		count++ ;
		if(count == 2)
			break ;
	}
}

void print1(queue_list *header)
{
	int count = 0 ;
	while(1)
	{
		printf("工作队列 work_serial_num: %d   hello world.....!\n",header->work_queue.work_serial_num);
		count++ ;
		if(count == 3)
			break ;
	}
}



//执行工作队列里的任务,每个任务都是一条线程 
work_st work_array[] = 
{
	{1,1,work_sleep,print},
	{2,2,work_sleep,print1},
	{3,3,work_sleep,print1},
};

int main(void)
{
	int ret = 0 ; 
	queue_list *queue_node = NULL ;
	queue_node =  malloc(sizeof(queue_list));
	//检查队列是否为空队列
	if(queue_node == NULL)
	{
		printf("队列申请内存为失败!\n");
		return -1 ;
	}
	printf("queue_node mem:0x%p\n",queue_node);
	//初始化工作队列头 
	Init_queue_header(queue_node,3);//QUEUE_LEN
	//入队
	printf("入队:\n");
	int i ;
	for(i = 0 ; i < 3 ; i++)
	{
		En_queue(queue_node , &work_array[i]);
	}

    //获取当前队列的长度
    printf("入队后:\n");
	ret = Get_queue_Length(queue_node); 
	printf("当前队列的长度:%d\n",ret);
	printf("----------------------------------------------------------------\n");
	while(queue_node)
		queue_node = De_queue(queue_node);
	
	if(queue_node == NULL)
		printf("节点是空的\n");
	else
		printf("节点不为空!\n");
	return 0 ;	
} 

//每一个工作所对应的时间 
void work_sleep(queue_list *header)
{
	printf("工作线程等待%ds后执行\n",header->work_queue.sec);
	sleep(header->work_queue.sec);
}


//初始化队列头节点 
void Init_queue_header(queue_list *header , int queue_max_length)
{
	queue_list *p = header ;	
	p->work_queue.work_serial_num = 0 ; 
	p->work_queue.queue_st = NULL ;
	p->queue_length = 0 ;
	p->queue_max_length = queue_max_length ;
	//默认初始化队列的时候,保存的NULL_flag是生效的 
	p->Null_flag = 1 ; 
	//默认是不生效的 
	p->Full_flag = 0 ;
	p->next = NULL ;
}

//检查队列是否为空 
//return 1 为空
//return !1  不为空 
int Check_queue_null(queue_list *header)
{
	queue_list *p = header ;
	if(NULL == p)
		return 1 ;
	if(p->Null_flag) 
	{
		p->Null_flag = 1;
		return p->Null_flag ;
	}
	p->Null_flag = 0 ;
	return p->Full_flag ;
}

//检查队列是否已经满了 
//       : 1  为满 
int Check_queue_full(queue_list *header , int queue_length)
{
	queue_list *p = header ;
	if(queue_length == p->Full_flag && queue_length == p->queue_max_length)
		return 1 ;
}

//获取当前队列的长度 
int Get_queue_Length(queue_list *header)
{
	queue_list *p = header ;
	if(NULL == p)
		return -1 ;
	return p->queue_length ;
}

//入队 
int En_queue(queue_list *header , work_st *workArray)
{
	queue_list *p = header ;
	queue_list *New = NULL ; 
	New = malloc(sizeof(queue_list));
	if(NULL == New){
		fprintf(stderr , "分配失败!\n");
		return ;
	}
	memset(New,0,sizeof(queue_list));
	p->queue_length++ ;
	if(p->queue_length > p->queue_max_length){
		printf("队列已满!不能再插入数据\n");
		p->Full_flag = 1 ;
		p->queue_length-- ;
		return -1;
	}
	New->work_queue.work_serial_num = workArray->work_serial_num ;
	printf("sec :%d\n",workArray->sec);
	New->work_queue.sec = workArray->sec ;
	New->work_queue.work_sleep = workArray->work_sleep ;
	New->work_queue.queue_st = workArray->queue_st ;
	p->Full_flag = 0 ;
	p->Null_flag = 0 ;
	while(NULL != p->next)  
		p = p->next ;
	New->next = p->next ; 
	p->next = New ; 
}

//逐个出队 
//出队 
queue_list *De_queue(queue_list *header)
{
	int ret ;
	int count = 0;
	int queue_length = 0;
	pthread_t tid ;
	queue_list *p = header ;
	queue_list *prev  = NULL ;
	//如果当前节点的下一个节点为空,则返回空 
	if(header->next == NULL){
		free(header);
		header = NULL ;
		printf("header mem:0x%p\n",header);
		return header ;
	}
	prev = p ;
	p = p->next ;
	ret = pthread_create(&tid, NULL , (void *)p->work_queue.work_sleep, p);
	if(ret < 0){
		printf("create work_thread fair!ssssssssssssssssssssssssss\n");
		return ;
	}
	//等待对应的工作结束 
	pthread_join(tid,NULL);
	
	//每出队一次,就相当于创建一条线程来执行对应的工作 
	ret = pthread_create(&tid, NULL , (void *)p->work_queue.queue_st, p);
	if(ret < 0){
		printf("create work_thread fair!ssssssssssssssssssssssssss\n");
		return ;
	}
	//等待对应的工作结束 
	pthread_join(tid,NULL);
	//获取队列的长度 
	queue_length = Get_queue_Length(header);
	queue_length = queue_length - 1;
	header->queue_length = queue_length ;
	printf("当前队列的长度:%d  %d\n",queue_length,header->queue_length);
	prev->next = p->next ;
	free(p);
	return prev ;
}
运行结果:


程序还可以继续优化改进,并实现项目应用,且听下回分解。








目录
相关文章
|
7月前
|
机器学习/深度学习 计算机视觉
YOLOv11改进策略【模型轻量化】| 替换骨干网络为 MobileViTv1高效的信息编码与融合模块,获取局部和全局信息
YOLOv11改进策略【模型轻量化】| 替换骨干网络为 MobileViTv1高效的信息编码与融合模块,获取局部和全局信息
261 9
YOLOv11改进策略【模型轻量化】| 替换骨干网络为 MobileViTv1高效的信息编码与融合模块,获取局部和全局信息
|
8月前
|
弹性计算 人工智能 自然语言处理
OS Copilot——面向未来的AI大模型
阿里云的智能助手`OS Copilot`是一款基于大模型构建的操作系统智能助手,支持自然语言问答、辅助命令执行、系统运维调优等功能。
356 8
OS Copilot——面向未来的AI大模型
|
7月前
|
网络安全 数据安全/隐私保护 网络架构
为何使用长效静态IP会出现高延迟现象?
在使用长效静态IP时,出现高延迟的原因主要包括:1. 网络距离远、网络拥堵和网络质量差等环境因素;2. 服务器负载高、性能低等服务器相关问题;3. 代理协议加密、网络配置不当等配置因素;4. 目标服务器响应慢。这些因素都会影响数据传输速度,导致延迟增加。希望以上分析能帮助解决您的问题。
212 8
|
8月前
|
程序员
Mac mini 通过键盘连接蓝牙鼠标
本文介绍了一种 Mac mini 通过纯键盘操作连接蓝牙鼠标的方法。
695 8
|
9月前
|
存储 虚拟化 iOS开发
VMware ESXi 8.0U3c 发布 - 领先的裸机 Hypervisor
VMware ESXi 8.0U3c 发布 - 领先的裸机 Hypervisor
308 3
VMware ESXi 8.0U3c 发布 - 领先的裸机 Hypervisor
|
9月前
|
数据可视化 算法 数据挖掘
Python量化投资实践:基于蒙特卡洛模拟的投资组合风险建模与分析
蒙特卡洛模拟是一种利用重复随机抽样解决确定性问题的计算方法,广泛应用于金融领域的不确定性建模和风险评估。本文介绍如何使用Python和EODHD API获取历史交易数据,通过模拟生成未来价格路径,分析投资风险与收益,包括VaR和CVaR计算,以辅助投资者制定合理决策。
484 15
|
NoSQL Redis
阿里云Redis集群版常见错误返回
阿里云Redis集群版常见的一些错误返回信息,帮助用户识别和排查问题。
5227 0
|
弹性计算 人工智能 安全
阿里云S级新游上云最佳实践
本篇通过S级新游案例介绍弹性计算上云的过程。
阿里云S级新游上云最佳实践
|
应用服务中间件
阿里云轻量应用服务器峰值带宽流量包超额收费规则
阿里云轻量应用服务器峰值带宽配备每月固定配额流量包套餐,当超过每月固定配额后流量按照价格为0.8元/GB来收取
1810 0
阿里云轻量应用服务器峰值带宽流量包超额收费规则