CN103176837B - 基于反转单链表的锁无关消息队列实现方法 - Google Patents
基于反转单链表的锁无关消息队列实现方法 Download PDFInfo
- Publication number
- CN103176837B CN103176837B CN201310102077.5A CN201310102077A CN103176837B CN 103176837 B CN103176837 B CN 103176837B CN 201310102077 A CN201310102077 A CN 201310102077A CN 103176837 B CN103176837 B CN 103176837B
- Authority
- CN
- China
- Prior art keywords
- lock
- pointer
- linked list
- pop
- null
- Prior art date
- Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
- Active
Links
Landscapes
- Information Retrieval, Db Structures And Fs Structures Therefor (AREA)
Abstract
基于反转单链表的锁无关消息队列实现方法,用于2线程服务器架构,包括a)基于反转单链表的锁无关消息队列的数据结构,b)基于所述数据结构实现的两个锁无关方法的操作函数:Push函数和Pop函数;2线程间通过所述锁无关消息队列,在所述锁无关方法下进行通讯。本发明在2个线程的服务器架构上,创新性的提出了基于反转单链表的锁无关消息队列的方法,并且没用使用任何昂贵的原子指令,将计算量降低到最低,使得执行效率非常高,通过实验,本发明提出的基于反转单链表的锁无关消息队列在2个线程的服务器架构下,执行速度是传统的基于锁的消息队列的数倍。
Description
技术领域
本发明属于计算机分布式领域,涉及在2线程架构的1个写线程和1个读线程程序中,实现锁无关的消息队列,为一种基于反转单链表的锁无关消息队列实现方法。
背景技术
在当前的服务器架构中,为了充分利用硬件资源,提高程序效率,大多数采取了多线程架构,而最常见的是有一个等待网络、终端等事件的等待线程,一个处理客户端消息、服务器主逻辑的主线程,一个用于2个线程间通讯的消息队列。
传统的消息队列采取了锁机制来保证同步、互斥。但锁机制会导致线程获取锁的时候,会进入等待睡眠状态,从而引发线程间的切换。而线程间的切换又是非常消耗资源的操作。在处理海量请求的服务器中,对消息队列的访问非常频繁,那么产生线程切换的概率也非常高,从而使得服务器处理效率直线下降。
而在锁无关数据结构的探索中,当今最常用的就是基于原子指令CAS(CompareAndSwap)来实现锁无关。虽然CAS的效率要比锁机制高很多,但CAS也并不是一个廉价的指令。在海量消息处理的服务器中,CAS带来的效率损失也不能不让使用者谨慎。
发明内容
本发明要解决的技术问题是:如何在抛弃锁机制以及CAS等昂贵的原子指令的前提下,实现2线程架构下的锁无关消息队列。
本发明的技术方案为:基于反转单链表的锁无关消息队列实现方法,用于2线程服务器架构,包括a)基于反转单链表的锁无关消息队列的数据结构,b)基于所述数据结构实现的两个锁无关方法的操作函数:Push函数和Pop函数;2线程间通过所述锁无关消息队列,在所述锁无关方法下进行通讯,其中:
1)、反转单链表的数据结构为:
structListElement{
structListElement*prev;
};
反转单链表中,每个链表元素只有一个指向其前一项链表元素的指针prev;
2)基于所述反转单链表的锁无关消息队列的数据结构为:
2a)设有一个指向反转单链表的链表头的head指针;
2b)设有一个指向反转单链表的链表尾的tail指针;
2c)设有一个指向上次Pop出去元素项的last指针;
3)基于所述锁无关消息队列的Push函数,包括以下几个要素:
3a)head指针只在最开始时为NULL,此时tail指针也为NULL,这种情况下,在Push第一个消息Push结束前,Pop函数总是返回NULL;
3b)在Push第一个消息时,对tail的赋值要在Push函数返回之前最后一个执行,使得Pop函数在Push函数结束前,总是返回NULL;
3c)每个新来的消息,都分配一个structListElement数据结构,即反转单链表的数据结构,并将其赋值;
3d)对于每一个消息项,Push函数在将其链入消息队列之前对其执行写操作,当其在消息队列里以后,Push函数不对其进行任何修改;
3e)只有Push函数永远不会被访问的元素项,才能由Pop释放内存;
3f)刚刚Pop出去的元素项不会被立即释放,而是存在last指针里,只有再次Pop出其他元素项时,last指针当前指向的元素项才会被释放;
4)基于所述锁无关消息队列的Pop函数,分为以下情况:
4a)如果tail==NULL&&last==NULL,那么消息队列处于未初始化状态,Pop返回NULL;
4b)如果tail==NULL&&last!=NULL,那么tail=last→prev,如果这时候tail还为NULL,说明消息队列为空,Pop返回NULL;
4c)如果tail!=NULL,那么tail指向的是一个正确的消息项,需要更新last指针,如果last指针之前不为NULL,则将其内存释放,让last指向最新释放的消息项,并将tail值更新为tail=tail→prev;
在2线程的服务器架构下,其中一个线程A为收发网络消息数据包的通讯线程,另一个线程B为处理服务器内部逻辑的主线程,这2个线程间通过所述锁无关消息队列和操作函数进行通讯:
a)定义一个基于反转单链表的锁无关消息队列:MsgQueue;
b)线程A从网络收到消息数据包Packet,执行MsgQueue.Push(Packet),即将收到的消息数据包通过Push函数添加进锁无关消息队列里;
c)线程B每次要处理服务器内部逻辑之前,都尝试通过pop函数从锁无关消息队
列MsgQueue中读取新的消息:Packet=MsgQueue.Pop()。
本发明在2个线程的服务器架构上,创新性的提出了基于反转单链表的锁无关消息队列的方法,并且没用使用任何昂贵的原子指令,例如:CAS、XADD之类,将计算量降低到最低,使得执行效率非常高,通过实验,本发明提出的基于反转单链表的锁无关消息队列在2个线程的服务器架构下,执行速度是传统的基于锁的消息队列的数倍。
附图说明
图1为本发明的反转单链表。
图2为本发明实施例在2线程架构下的实验结果对比图。
图3为本发明实施例的push结果对比图。
图4为本发明实施例的pop结果对比图。
具体实施方式
传统的2个线程的服务器架构下,包括:一个接收外部事件的等待线程A,一个处理事件、主逻辑的主线程B,一个消息队列。其中,线程A等待网络、终端等消息到来(Message),一般是客户端发来的事件(请求一个服务等等),并将收到的消息放入消息队列;线程B以每秒N帧的频率处理消息队列中的事件,以及处理服务器本身的主逻辑(例如:网络游戏服务器需要处理游戏世界的主逻辑–怪物的AI、虚拟世界天气变化等等)。在这种架构下,线程A、B都要对消息队列操作,在传统服务器中,大多数采取了锁机制处理消息队列的同步、互斥。而锁机制低下的效率一直为人诟病。
传统锁机制消息队列伪码如下:
基于上述传统传统锁机制消息队列的操作函数Push和Pop分别为:
本发明基于反转单链表的锁无关消息队列实现方法,包括a)基于反转单链表的锁无关消息队列的数据结构,b)基于所述数据结构实现的两个锁无关方法的操作函数:Push函数和Pop函数;2线程间通过所述锁无关消息队列,在所述锁无关方法下进行通讯。
1、反转单链表:
反转单链表如图1,传统的单链表,是从链表头部,依次有next指针指向下一项元素,最后一项元素的next指针为NULL。本发明创新地将next指针反转,即每项元素只有一个prev指针指向其前一项元素。
下面用伪码的形式,给出用反转链表形式实现的无锁消息队列:
其中,反转单链表包括以下元素:
(1)定义一个数据结构,是链表中的每一项,它包括指向前一个元素的prev指针和数据项;
(2)为了可以在O(1)时间里进行Push、Pop操作,定义了指向第一个节点的head指针和一个指向最后一个节点的tail指针,这里O(1)时间是指常数时间,为本领域公知技术;
(3)为了实现无锁消息队列,还需有一个指向上次Pop出去元素项的指针last;
(4)被Pop出去的元素,不会立即释放内存,而是记录在last指针里,当有新的元素被Pop出去后,上次记录在last指针里的元素内存才被释放,最新Pop出去的元素记录在last指针里。
即基于反转单链表的锁无关消息队列的特征为:
a)有一个指向反转单链表链表头的head指针;
b)有一个指向反转单链表链表尾的tail指针;
c)有一个指向上次Pop出去元素项的last指针。
2、用伪码实现无锁消息队列的Push函数,Push函数用于添加新消息至锁无关消息队列。
Push函数包括以下几个要素:
(1)head指针只在最开始时为NULL,此时tail指针也为NULL,这种情况下,在Push第一个消息Push结束前,Pop函数总是返回NULL;
(2)在head指针为NULL的情况下,对tail的赋值要在函数return之前最后一个执行,就可以保证对tail赋值以后,消息队列是正确的;
(3)Push函数的作用,只是将新的消息链在消息队列的头部,它并不关心tail指针,也不关心消息什么时候释放内存,也不关心互斥、同步相关的东西,同步、互斥相关的都在Pop里关心;
(4)每个新来的消息,都会分配一个structListElement数据结构,并将其赋值;
(5)对于每一个消息项,Push函数会在将其链入消息队列之前对其执行写操作,当其在链表里以后,Push函数不会对其进行任何修改(除了prev字段),而当其在消息队列以后,才会由Pop函数执行读操作,并且对于prev字段,只有Push函数对其执行写操作,Pop函数只对其执行读操作,所以Push函数和Pop函数不会发生写冲突;
(6)只有Push函数永远不会被访问的对象,才能由Pop释放内存:Push函数只会访问最近Push进去的元素项(更改其prev字段),所以,新Pop出去的元素项不会被释放,而是存在last指针里,只有再次Pop出其他元素项时,last指针指向的元素项才会被释放。
3、用伪码实现无锁消息队列的Pop函数,Pop函数用于从锁无关消息队列中释放弹出消息:
Pop函数要比Push函数复杂的多,分为以下情况:
a)如果tail==NULL&&last==NULL,那么消息队列处于未初始化状态(还从没Push任何值),那么Pop返回NULL;
b)如果tail==NULL&&last!=NULL,那么tail=last→prev,如果这时候tail还为NULL,说明消息队列为空,Pop返回NULL;
c)如果tail!=NULL,那么tail指向的是一个正确的消息项,需要更新last指针,如果last指针之前不为NULL,则将其内存释放,让last指向最新释放的消息项,并将tail值更新为tail=tail→prev。
本发明建立了基于反转单链表的锁无关消息队列的数据结构,再基于所述数据结构建立了两个锁无关方法的操作函数:Push函数和Pop函数,使消息队列在2线程服务器架构下执行速度得到大幅提高:
(1)Push函数的作用,就是将新来的消息链在消息队列头部,它的执行结果是形成一个反转单链表;
(2)Pop函数的tail,是依次从后往前,按Push的顺序,返回消息;
(3)在Push和Pop同时调用时,保证了Push不会受到影响,反转单链表会一直正确的被建立;
(4)如果Push的速度大于Pop的速度,那么每次Pop出去的元素项,都不是Push会访问到的元素项,而且tail指针的值一直都是有效的,那么就不会引起同步、互斥问题;
(5)如果Push的速度小于Pop的速度,那么tail指针会经常处于NULL状态,为了能使Pop正常执行,用一个last指针记录上次Pop出去的元素项,每次tail指针为NULL时,都试图从last→prev取出正确的元素项。
将本发明所述锁无关消息队列和传统的基于锁的消息队列进行实验对比。
实验设置2个线程,一个写线程、一个读线程。写线程持续不断的向消息队列里面写入数据,读线程持续不断的从消息队列里读出数据。写线程持续写入N条数据后退出,N取值为:1百万、2百万、……、1千万,读线程持续从消息队列中读出N条数据后退出。
实验平台为:
AMDA6-4400MAPUwithRadeon(tm)HDGraphics单核单CPU、Linuxversion3.5.0-25-generic(builddkomainu)、(gccversion4.7.2(Ubuntu/Linaro4.7.2-2ubuntu1))、#39-UbuntuSMPMonFeb2518:26:58UTC2013。
整个过程花费的时间如下图2所示,X轴代表循环的次数:从1百万到1千万,Y轴代表花费的时间,单位:微秒。可以看出,随着循环次数的增加,传统的基于锁的消息队列比本发明锁无关消息队列耗费的时间增长更快。
平均每次Push花费的时间统计如图3所示,X轴代表循环次数,Y轴代表平均每次Push时间(微秒)。可以看出,本发明所述的锁无关消息队列要比传统的基于锁的消息队列快1倍。
平均Pop花费的时间如图4所示,X轴代表循环次数,Y轴代表平均每次Pop时间(微秒)。可以看出,本发明所述锁无关消息队列要比传统的基于锁的消息队列快1倍多。
Claims (1)
1.基于反转单链表的锁无关消息队列实现方法,用于2线程服务器架构,其特征包括:a)基于反转单链表的锁无关消息队列的数据结构,b)基于所述数据结构实现的两个锁无关方法的操作函数:Push函数和Pop函数;2线程间通过所述锁无关消息队列,在所述锁无关方法下进行通讯,其中:
1)、反转单链表的数据结构中,每个链表元素只有一个指向其前一项链表元素的指针prev;
2)基于所述反转单链表的锁无关消息队列的数据结构为:
2a)设有一个指向反转单链表的链表头的head指针;
2b)设有一个指向反转单链表的链表尾的tail指针;
2c)设有一个指向上次Pop出去元素项的last指针;
3)基于所述锁无关消息队列的Push函数,包括以下几个要素:
3a)head指针只在最开始时为NULL,此时tail指针也为NULL,这种情况下,在Push第一个消息Push结束前,Pop函数总是返回NULL;
3b)在Push第一个消息时,对tail的赋值要在Push函数返回之前最后一个执行,使得Pop函数在Push函数结束前,总是返回NULL;
3c)每个新来的消息,都分配一个structListElement数据结构,即反转单链表的数据结构,并将其赋值;
3d)对于每一个消息项,Push函数在将其链入消息队列之前对其执行写操作,当其在消息队列里以后,Push函数不对其除prev字段以外内容进行任何修改;
3e)只有Push函数永远不会被访问的元素项,才能由Pop释放内存;
3f)刚刚Pop出去的元素项不会被立即释放,而是存在last指针里,只有再次Pop出其他元素项时,last指针当前指向的元素项才会被释放;
4)基于所述锁无关消息队列的Pop函数,分为以下情况:
4a)如果tail指针为NULL且last指针为NULL,那么消息队列处于未初始化状态,Pop返回NULL;
4b)如果tail指针为NULL且last指针不为NULL,那么tail指针赋值为last指针且last指针指向指针prev,如果这时候tail还为NULL,说明消息队列为空,Pop返回NULL;
4c)如果tail指针不为NULL,那么tail指向的是一个正确的消息项,需要更新last指针,如果last指针之前不为NULL,则将其内存释放,让last指向最新释放的消息项,并将tail值更新为指针prev的内容;
在2线程的服务器架构下,其中一个线程A为收发网络消息数据包的通讯线程,另一个线程B为处理服务器内部逻辑的主线程,这2个线程间通过所述锁无关消息队列和操作函数进行通讯:
a)定义一个基于反转单链表的锁无关消息队列:MsgQueue;
b)线程A从网络收到消息数据包Packet,执行MsgQueue.Push(Packet),即将收到的消息数据包通过Push函数添加进锁无关消息队列里;
c)线程B每次要处理服务器内部逻辑之前,都尝试通过pop函数从锁无关消息队列MsgQueue中读取新的消息:Packet=MsgQueue.Pop()。
Priority Applications (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN201310102077.5A CN103176837B (zh) | 2013-03-27 | 2013-03-27 | 基于反转单链表的锁无关消息队列实现方法 |
Applications Claiming Priority (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN201310102077.5A CN103176837B (zh) | 2013-03-27 | 2013-03-27 | 基于反转单链表的锁无关消息队列实现方法 |
Publications (2)
Publication Number | Publication Date |
---|---|
CN103176837A CN103176837A (zh) | 2013-06-26 |
CN103176837B true CN103176837B (zh) | 2016-06-08 |
Family
ID=48636738
Family Applications (1)
Application Number | Title | Priority Date | Filing Date |
---|---|---|---|
CN201310102077.5A Active CN103176837B (zh) | 2013-03-27 | 2013-03-27 | 基于反转单链表的锁无关消息队列实现方法 |
Country Status (1)
Country | Link |
---|---|
CN (1) | CN103176837B (zh) |
Families Citing this family (5)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN103631665B (zh) * | 2013-12-12 | 2017-11-07 | 北京奇安信科技有限公司 | 一种基于消息队列的线程间通信的方法和系统 |
CN107181789A (zh) * | 2017-03-31 | 2017-09-19 | 北京奇艺世纪科技有限公司 | 一种分布式锁实现方法及装置 |
CN108494704A (zh) * | 2018-03-05 | 2018-09-04 | 电子科技大学 | 一种基于通道的双缓冲技术的实时数据流处理方法 |
CN110362348A (zh) * | 2018-04-09 | 2019-10-22 | 武汉斗鱼网络科技有限公司 | 一种队列存取数据的方法、装置及电子设备 |
CN109271242A (zh) * | 2018-08-28 | 2019-01-25 | 百度在线网络技术(北京)有限公司 | 基于队列的数据处理方法、装置、设备和介质 |
Citations (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN101819596A (zh) * | 2010-04-28 | 2010-09-01 | 烽火通信科技股份有限公司 | 一种基于内存的xml脚本缓存容器 |
CN102880507A (zh) * | 2012-09-12 | 2013-01-16 | 科立讯通信股份有限公司 | 一种链式结构消息申请及分发的方法 |
-
2013
- 2013-03-27 CN CN201310102077.5A patent/CN103176837B/zh active Active
Patent Citations (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN101819596A (zh) * | 2010-04-28 | 2010-09-01 | 烽火通信科技股份有限公司 | 一种基于内存的xml脚本缓存容器 |
CN102880507A (zh) * | 2012-09-12 | 2013-01-16 | 科立讯通信股份有限公司 | 一种链式结构消息申请及分发的方法 |
Also Published As
Publication number | Publication date |
---|---|
CN103176837A (zh) | 2013-06-26 |
Similar Documents
Publication | Publication Date | Title |
---|---|---|
CN103176837B (zh) | 基于反转单链表的锁无关消息队列实现方法 | |
Hellings et al. | Byshard: Sharding in a byzantine environment | |
CN110691062B (zh) | 一种数据写入方法、装置及其设备 | |
CN105897589B (zh) | 用于cuckoo散列流查找的并发性的方法及设备 | |
US8751737B2 (en) | Method and apparatus for using a shared ring buffer to provide thread synchronization in a multi-core processor system | |
US10693787B2 (en) | Throttling for bandwidth imbalanced data transfers | |
US20100198920A1 (en) | High performant information sharing and replication for single-publisher and multiple-subscriber configuration | |
US20130103654A1 (en) | Global dictionaries using universal primitives | |
CN105634958A (zh) | 基于多核系统的报文转发方法和装置 | |
US20140359232A1 (en) | System and method of a shared memory hash table with notifications | |
US10331500B2 (en) | Managing fairness for lock and unlock operations using operation prioritization | |
US10203995B2 (en) | Method or system for access to shared resource | |
US9246836B2 (en) | Single producer, single consumer lockless FIFO/LIFO queue | |
EP2983089B1 (en) | Method, device, and chip for implementing mutually exclusive operation of multiple threads | |
CN113076304A (zh) | 一种分布式版本管理方法、装置和系统 | |
US9817594B2 (en) | System and method for broadcasting data to multiple hardware forwarding engines | |
US20140289744A1 (en) | Transaction capable queuing | |
US9384047B2 (en) | Event-driven computation | |
US20090249343A1 (en) | System, method, and computer program product for receiving timer objects from local lists in a global list for being used to execute events associated therewith | |
CN112437125A (zh) | 信息并发处理方法、装置、电子设备及存储介质 | |
CN112714492A (zh) | 一种uwb数据包处理方法、系统、电子设备及其存储介质 | |
US10122643B2 (en) | Systems and methods for reorganization of messages in queuing systems | |
US20230333878A1 (en) | Request processing methods and apparatuses, computing device and storage medium | |
US20230056344A1 (en) | Systems and methods for processing out-of-order events | |
US10496625B1 (en) | Ordering system that employs chained ticket release bitmap having a protected portion |
Legal Events
Date | Code | Title | Description |
---|---|---|---|
C06 | Publication | ||
PB01 | Publication | ||
C10 | Entry into substantive examination | ||
SE01 | Entry into force of request for substantive examination | ||
C14 | Grant of patent or utility model | ||
GR01 | Patent grant |