多线程环境下不安全的消息队列存取---线程不同步会造成隐患

本文主要是介绍多线程环境下不安全的消息队列存取---线程不同步会造成隐患,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        前面, 我们把消息队列存取都放在主线程中, 而在实际应用中, 很多时候, 存消息队列在主线程, 取消息队列在其他线程(如网络线程)。 下面, 我们将之前的程序改为多线程程序:

 

#include <windows.h>
#include <iostream>
using namespace std;#define Rectangle MyRectangle  // 避免Rectangle与Windows中的Rectangle冲突// 对象的id值
typedef enum
{ErrorId = -1,IntegerId = 1,PointId = 2,RectangeId = 3,
}ObjectID;// 基类
struct Basic
{ObjectID id;virtual Basic *copy() = 0; // 纯虚函数
};// 整数类
struct Integer : public Basic
{int a;Basic *copy(){Integer *p = new Integer;p->a = ((Integer*)this)->a;p->id = ((Integer*)this)->id;return p;}
};// 点类
struct Point : public Basic
{int x;int y;Basic *copy(){Point *p = new Point;p->x = ((Point*)this)->x;p->y = ((Point*)this)->y;p->id = ((Point*)this)->id;return p;}
};// 矩形类
struct Rectangle : public Basic
{Point point;int width;int height;Basic *copy(){Rectangle *p = new Rectangle;p->point.x = ((Rectangle*)this)->point.x;p->point.y = ((Rectangle*)this)->point.y;p->width = ((Rectangle*)this)->width;p->height = ((Rectangle*)this)->height;p->id = ((Rectangle*)this)->id;return p;}
};// 抽象对象的共同点, 构造成新的结点, 便于链接
typedef struct node
{node *next;Basic *pBasic;
}Node;Node *head = NULL;  // 指向第一结点(采用不带头结点的链表)// 往链式消息队列中塞消息
Node *addToMsgQueue(Basic* pb)
{Node *pn = new Node;Node *qn = NULL;Basic *p = pb->copy(); // 多态性if(NULL == head){head = pn;}else{qn = head;while(NULL != qn->next){qn = qn->next;}qn->next = pn;}pn->pBasic = p;   // 千万别忘记啊pn->next = NULL;  // 千万别忘记啊return head;
}// 从链式消息队列中取出消息(结点)
Node *getMsgFromQueue()
{if(NULL == head){return NULL;}Node *pn = head;head = head->next;return pn;
} // 线程函数
DWORD WINAPI ThreadFun(LPVOID pM)
{Node *p = NULL;// 从消息队列中取出消息while(1){p = getMsgFromQueue();if(NULL == p){Sleep(100);continue;}// 对指针进行还原switch(p->pBasic->id) {case IntegerId:{cout << ((Integer*)(p->pBasic))->a << endl;break;}case PointId:{cout << ((Point *)(p->pBasic))->x << endl;cout << ((Point *)(p->pBasic))->y << endl;break;}case RectangeId:{cout << ((Rectangle *)(p->pBasic))->point.x << endl;cout << ((Rectangle *)(p->pBasic))->point.y << endl;cout << ((Rectangle *)(p->pBasic))->width << endl;cout << ((Rectangle *)(p->pBasic))->height << endl;break;}default:{break;}}}return 0;
}// 主线程
int main()
{HANDLE handle = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);CloseHandle(handle);// 定义三个对象并赋值Integer i;Point po;Rectangle rect;i.id = IntegerId;po.id = PointId;rect.id = RectangeId;i.a = 11;po.x = 22;po.y = 33;rect.point.x = 44;rect.point.y = 55;rect.width = 66;rect.height = 77;// 塞入消息队列while(1){	addToMsgQueue(&i);addToMsgQueue(&po);addToMsgQueue(&rect);Sleep(2000);}return 0;
}

      结果为:

 

11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77
11
22
33
44
55
66
77

...

       看似得到了正确的结果, 但是, 上述程序是有问题的, 加入主线程正在存, 子线程正在取, 那岂不是乱套了么。 当然, 上面的程序侥幸没有出现这种情况, 为了说明这种情况, 我在addToMsgQueue函数中, 故意加一个睡眠语句, 再看看:

 

#include <windows.h>
#include <iostream>
using namespace std;#define Rectangle MyRectangle  // 避免Rectangle与Windows中的Rectangle冲突// 对象的id值
typedef enum
{ErrorId = -1,IntegerId = 1,PointId = 2,RectangeId = 3,
}ObjectID;// 基类
struct Basic
{ObjectID id;virtual Basic *copy() = 0; // 纯虚函数
};// 整数类
struct Integer : public Basic
{int a;Basic *copy(){Integer *p = new Integer;p->a = ((Integer*)this)->a;p->id = ((Integer*)this)->id;return p;}
};// 点类
struct Point : public Basic
{int x;int y;Basic *copy(){Point *p = new Point;p->x = ((Point*)this)->x;p->y = ((Point*)this)->y;p->id = ((Point*)this)->id;return p;}
};// 矩形类
struct Rectangle : public Basic
{Point point;int width;int height;Basic *copy(){Rectangle *p = new Rectangle;p->point.x = ((Rectangle*)this)->point.x;p->point.y = ((Rectangle*)this)->point.y;p->width = ((Rectangle*)this)->width;p->height = ((Rectangle*)this)->height;p->id = ((Rectangle*)this)->id;return p;}
};// 抽象对象的共同点, 构造成新的结点, 便于链接
typedef struct node
{node *next;Basic *pBasic;
}Node;Node *head = NULL;  // 指向第一结点(采用不带头结点的链表)// 往链式消息队列中塞消息
Node *addToMsgQueue(Basic* pb)
{Node *pn = new Node;Node *qn = NULL;Basic *p = pb->copy(); // 多态性if(NULL == head){head = pn;}else{qn = head;while(NULL != qn->next){qn = qn->next;}Sleep(20); // 故意加的语句, 用于构造异常场景 qn->next = pn;}pn->pBasic = p;   // 千万别忘记啊pn->next = NULL;  // 千万别忘记啊return head;
}// 从链式消息队列中取出消息(结点)
Node *getMsgFromQueue()
{if(NULL == head){return NULL;}Node *pn = head;head = head->next;return pn;
} // 线程函数
DWORD WINAPI ThreadFun(LPVOID pM)
{Node *p = NULL;// 从消息队列中取出消息while(1){p = getMsgFromQueue();if(NULL == p){Sleep(100);continue;}// 对指针进行还原switch(p->pBasic->id) {case IntegerId:{cout << ((Integer*)(p->pBasic))->a << endl;break;}case PointId:{cout << ((Point *)(p->pBasic))->x << endl;cout << ((Point *)(p->pBasic))->y << endl;break;}case RectangeId:{cout << ((Rectangle *)(p->pBasic))->point.x << endl;cout << ((Rectangle *)(p->pBasic))->point.y << endl;cout << ((Rectangle *)(p->pBasic))->width << endl;cout << ((Rectangle *)(p->pBasic))->height << endl;break;}default:{break;}}}return 0;
}// 主线程
int main()
{HANDLE handle = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);CloseHandle(handle);// 定义三个对象并赋值Integer i;Point po;Rectangle rect;i.id = IntegerId;po.id = PointId;rect.id = RectangeId;i.a = 11;po.x = 22;po.y = 33;rect.point.x = 44;rect.point.y = 55;rect.width = 66;rect.height = 77;// 塞入消息队列while(1){	addToMsgQueue(&i);addToMsgQueue(&po);addToMsgQueue(&rect);Sleep(2000);}return 0;
}

      程序结果为:

 

11
44
55
66
77
11
22
33
44
55
66
77
...

      看看,看看, 产生异常了吧, 主要原因是线程不同步问题, 在后续博文中, 我们将介绍线程安全的情况, 那时, 我们将考虑线程同步。




 


 

这篇关于多线程环境下不安全的消息队列存取---线程不同步会造成隐患的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/684691

相关文章

Python如何自动生成环境依赖包requirements

《Python如何自动生成环境依赖包requirements》:本文主要介绍Python如何自动生成环境依赖包requirements问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录生成当前 python 环境 安装的所有依赖包1、命令2、常见问题只生成当前 项目 的所有依赖包1、

SpringKafka错误处理(重试机制与死信队列)

《SpringKafka错误处理(重试机制与死信队列)》SpringKafka提供了全面的错误处理机制,通过灵活的重试策略和死信队列处理,下面就来介绍一下,具有一定的参考价值,感兴趣的可以了解一下... 目录引言一、Spring Kafka错误处理基础二、配置重试机制三、死信队列实现四、特定异常的处理策略五

Redis在windows环境下如何启动

《Redis在windows环境下如何启动》:本文主要介绍Redis在windows环境下如何启动的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Redis在Windows环境下启动1.在redis的安装目录下2.输入·redis-server.exe

Pytest多环境切换的常见方法介绍

《Pytest多环境切换的常见方法介绍》Pytest作为自动化测试的主力框架,如何实现本地、测试、预发、生产环境的灵活切换,本文总结了通过pytest框架实现自由环境切换的几种方法,大家可以根据需要进... 目录1.pytest-base-url2.hooks函数3.yml和fixture结论你是否也遇到过

在Android平台上实现消息推送功能

《在Android平台上实现消息推送功能》随着移动互联网应用的飞速发展,消息推送已成为移动应用中不可或缺的功能,在Android平台上,实现消息推送涉及到服务端的消息发送、客户端的消息接收、通知渠道(... 目录一、项目概述二、相关知识介绍2.1 消息推送的基本原理2.2 Firebase Cloud Me

Python从零打造高安全密码管理器

《Python从零打造高安全密码管理器》在数字化时代,每人平均需要管理近百个账号密码,本文将带大家深入剖析一个基于Python的高安全性密码管理器实现方案,感兴趣的小伙伴可以参考一下... 目录一、前言:为什么我们需要专属密码管理器二、系统架构设计2.1 安全加密体系2.2 密码强度策略三、核心功能实现详解

浅谈配置MMCV环境,解决报错,版本不匹配问题

《浅谈配置MMCV环境,解决报错,版本不匹配问题》:本文主要介绍浅谈配置MMCV环境,解决报错,版本不匹配问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录配置MMCV环境,解决报错,版本不匹配错误示例正确示例总结配置MMCV环境,解决报错,版本不匹配在col

SpringKafka消息发布之KafkaTemplate与事务支持功能

《SpringKafka消息发布之KafkaTemplate与事务支持功能》通过本文介绍的基本用法、序列化选项、事务支持、错误处理和性能优化技术,开发者可以构建高效可靠的Kafka消息发布系统,事务支... 目录引言一、KafkaTemplate基础二、消息序列化三、事务支持机制四、错误处理与重试五、性能优

SpringIntegration消息路由之Router的条件路由与过滤功能

《SpringIntegration消息路由之Router的条件路由与过滤功能》本文详细介绍了Router的基础概念、条件路由实现、基于消息头的路由、动态路由与路由表、消息过滤与选择性路由以及错误处理... 目录引言一、Router基础概念二、条件路由实现三、基于消息头的路由四、动态路由与路由表五、消息过滤

Spring Boot3虚拟线程的使用步骤详解

《SpringBoot3虚拟线程的使用步骤详解》虚拟线程是Java19中引入的一个新特性,旨在通过简化线程管理来提升应用程序的并发性能,:本文主要介绍SpringBoot3虚拟线程的使用步骤,... 目录问题根源分析解决方案验证验证实验实验1:未启用keep-alive实验2:启用keep-alive扩展建