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

来源:互联网 发布:slack mac下载 编辑:程序博客网 时间:2024/05/17 06:01

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

#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
...

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







0 0
原创粉丝点击