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

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

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

 

#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

相关文章

RabbitMQ消息总线方式刷新配置服务全过程

《RabbitMQ消息总线方式刷新配置服务全过程》SpringCloudBus通过消息总线与MQ实现微服务配置统一刷新,结合GitWebhooks自动触发更新,避免手动重启,提升效率与可靠性,适用于配... 目录前言介绍环境准备代码示例测试验证总结前言介绍在微服务架构中,为了更方便的向微服务实例广播消息,

Nginx安全防护的多种方法

《Nginx安全防护的多种方法》在生产环境中,需要隐藏Nginx的版本号,以避免泄漏Nginx的版本,使攻击者不能针对特定版本进行攻击,下面就来介绍一下Nginx安全防护的方法,感兴趣的可以了解一下... 目录核心安全配置1.编译安装 Nginx2.隐藏版本号3.限制危险请求方法4.请求限制(CC攻击防御)

Windows环境下解决Matplotlib中文字体显示问题的详细教程

《Windows环境下解决Matplotlib中文字体显示问题的详细教程》本文详细介绍了在Windows下解决Matplotlib中文显示问题的方法,包括安装字体、更新缓存、配置文件设置及编码調整,并... 目录引言问题分析解决方案详解1. 检查系统已安装字体2. 手动添加中文字体(以SimHei为例)步骤

Java JDK1.8 安装和环境配置教程详解

《JavaJDK1.8安装和环境配置教程详解》文章简要介绍了JDK1.8的安装流程,包括官网下载对应系统版本、安装时选择非系统盘路径、配置JAVA_HOME、CLASSPATH和Path环境变量,... 目录1.下载JDK2.安装JDK3.配置环境变量4.检验JDK官网下载地址:Java Downloads

Linux线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

SpringBoot线程池配置使用示例详解

《SpringBoot线程池配置使用示例详解》SpringBoot集成@Async注解,支持线程池参数配置(核心数、队列容量、拒绝策略等)及生命周期管理,结合监控与任务装饰器,提升异步处理效率与系统... 目录一、核心特性二、添加依赖三、参数详解四、配置线程池五、应用实践代码说明拒绝策略(Rejected

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互