[译]预留位置队列PRQueue:多线程程序中消息输入队列和消息输出队列保持同序...

译自: http://accu.org/var/uploads/journals/overload101.pdf

        在多线程应用程序中,要求消息输入队列和消息输出队列顺序要求保持一致,而忽略多线程并发处理的顺序,这种情况是比较难处理的。在本文中,作者设计了一种新型解决方案:PRQueue(预留位置队列),较很好的解决这个问题。

        PRQueue是使用c++的两个STL的deque还有pthread线程库实现的,并且在例子中使用了两个简单的类-Mutex和Lock来展示这个逻辑。StringMsg类表现一个样本消息,QueueTest类用来测试。

        我选择STL的deque是因为deque拥有很多必要的操作(包括operator[])来实现PRQueue。特别的,有一点很重要的是push_back和pop_front()操作对于deque的元素的指针或引用来说都是有效地。

        这里有一个简单的例子来展示PRQueue的作用。首先,我们需要记录大量的多域的消息流。转化文本字符串的数字域是一个慢的且并不关键的过程,因此我们决定将这份任务分派给能够生成日志的线程来做。处理的流程如下图所示:

        因为消息的核心处理过程是发生在多线程中,消息的就绪顺序也许跟原始的输入队列不同。例如:如果一个线程从输入队列中拿走了一个报文消息后进入休眠状态,另外一个线程取出下一个报文消息,在第一个线程之前就运行完成处理这个报文消息,并把这个报文放入输出队列中。因此,这输出日志也许会无序了(我们假设消息被处理完之后才日志)。

       使用PRQueue 则以上这种场景就能避免,它将会确保在输出队列中报文的顺序和输入队列中保持一致,而不管线程处理报文的顺序如何。PRQueue的基本逻辑比较简单,当下一个报文从输入队列中取出的时候,仍然放入锁中,下一个push_back的位置。然后释放锁,并且继续处理。在这个消息报文完全处理完之后,先前请求的位置用来把这个消息放入输出队列。

     PRQueue 使用两个队列构造:'data’ 和'filled’。

     'filled’队列的一个元素使用数据填充并且能够从PRQueue弹出。一个封装类DataQueue是'data’和'filled’队列的装载器。 这种设计允许我们在确切的实现过程中分离线程安全代码,因此用户不需要关心任何锁/解锁的逻辑。

     下面让我们更详细的讨论PRQUEUE。

     PRQueue方法主要做两件事情:它从输入队列中弹出数据,并且在输出队列中保留一个位置。PUSH方法使用之前保存的位置在输出队列中保存数据。

     为了使用多线程测试PRQUEUE,PROCESS_MSG执行。它从输入队列中取出一个StringMsg,通过调用StringMsg::process()方法来处理这个消息并且push这个报文。

//------------
static void* process_msg( void* arg)
{int thidx = ++Thidx;QueueTest* quetest =(QueueTest*)arg;Msg* msg;PRQueue< Msg*>::position pos;cout << "Input thread=" << thidx << " started" << endl;for( ;;){// Wait for next message appeared in input queue,// pop it up and get push position allocated in output queuequetest->input_que.pop( msg, quetest->output_que, pos);// Process messagemsg->process( thidx);// Push processed message into output queue using acquired position	quetest->output_que.push( msg, pos);}    return NULL;
}//


POP方法不仅等待输入队列中下一个报文的到来,也通过查看’filled’队列中元素来检查这个报文是否准备弹出了。如果数据还没有填充,pop将继续阻塞等待

POP方法的处理逻辑:

1.      锁定输入队列

2.      如果输入队列非空并且顶层元素填充了数据,则pop它(否则释放掉锁并继续睡眠)

3.      锁住输出队列

4.      保留输出队列底部位置

5.      解锁输出队列

6.      解锁输入队列

代码段如下:

    // Pop data from input queue and reserve position in the output queuevoid pop( DT& data, PRQueue& outque, position& pos){Lock lk( m_mux);while( true) {if( m_que.pop( data))break;wait_while_empty();}outque.reserve_pos( pos);}


PUSH方法拷贝数据到输出队列的保留位置并且设置'filled’指示为真。它也通过发送一个通知信号来释放掉等待一个条件变量的线程。

代码段如下:

    // Simple pushvoid push( const DT& data) {Lock lk( m_mux);m_que.push( data);notify_not_empty();} 

       现在,这个消息报文按序的到达了输出队列,如果我们想要更深的扩展我们的处理链的话,可以在后面再加上一个PRQueue。在以上的测试用例中我们不会这样做:我们使用一个单一的线程简单的从输出队列中读取处理完的报文并将它们打印出来。在最后的一步,只简单的使用了pop方法(未使用第二、第三个参数:指向输出队列和保留的位置的值)。

//------------
static void* print_msg( void* arg)
{QueueTest* quetest =(QueueTest*)arg;Msg* msg;cout << "Output thread started" << endl;for( ;;){quetest->output_que.pop( msg);msg->print();delete msg;}    return NULL;
}//


总结

       在多线程应用程序中,当处理的消息流顺序需要保证的时候,本文所说的预留位置队列将会是有用的。PRQueue将会确保输出队列中报文顺序同输入队列保持一致,因为在输出队列中下一个push_back的位置在输入队列取出报文的时候就同步的保留了。当报文消息处理完成之后,所保留的位置随后将会被数据填充。

注:完整代码于此处下载:http://accu.org/content/journals/ol101/prqueue.zip(译此文时,时间较仓促,因此译文很粗糙,待时间较宽松时再细细校验)。

 

作者:lgp88 发表于2012-3-12 13:57:53 原文链接
阅读:88 评论:0 查看评论

 

转载于:https://www.cnblogs.com/moonlove/archive/2012/03/12/2509149.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/434615.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 前端工作内容_java前端、java后端、java全栈工作主要内容是什么?哪个薪资高?...

摘要最近&#xff0c;听了一场关于java全栈工程师职位的简介说明&#xff0c;里面很清楚的说明了一下前端&#xff0c;后端&#xff0c;全栈都是做什么工作的。其实&#xff0c;想做这个行业&#xff0c;就应该了解职能以及技能需求&#xff0c;这样学习才能更高效。我知道一些…

OpenGL ES 2.0 for iPhone Tutorial

来源&#xff1a;http://www.raywenderlich.com/3664/opengl-es-2-0-for-iphone-tutorial If youre new here, you may want to subscribe to my RSS feed or follow me on Twitter. Thanks for visiting! Learn how to use OpenGL ES 2.0 from the ground up! OpenGL ES is th…

java种子填充_种子填充实例运行出问题

种子填充实例运行出问题import java.awt.*;import java.applet.*;import java.awt.image.ImageProducer;import java.awt.image.MemoryImageSource;import java.util.Stack;public class scanseed extends Applet {private static final long serialVersionUID 1L;int redColo…

java幂等性原理_Java接口幂等性设计原理解析

在微服务架构下&#xff0c;我们在完成一个订单流程时经常遇到下面的场景&#xff1a;一个订单创建接口&#xff0c;第一次调用超时了&#xff0c;然后调用方重试了一次在订单创建时&#xff0c;我们需要去扣减库存&#xff0c;这时接口发生了超时&#xff0c;调用方重试了一次…

java web Jersey_使用CXF和Jersey框架来进行Java的WebService编程

CXFCXF是在xfire的基础上实现的。1)首先呢&#xff0c;还是包的问题&#xff0c;在http://cxf.apache.org/download.html这里可以下到最新版的CXF&#xff0c;当然&#xff0c;我用的是最新版的。接下来还是那句废话&#xff0c;建WEB项目&#xff0c;放入JAR包。而JAR包我们就…

S3C2440与SDRAM的地址连线分析

S3C2440有27根地址线ADDR[26:0]&#xff0c;8根片选信号ngcs0-ngcs7,对应bank0-bank7&#xff0c;当访问bankx的地址空间&#xff0c;ngcsx引脚为低电平&#xff0c;选中外设。 2^272^7 * 2^10 * 2^10 128Mbyte 8*128Mbyte 1Gbyte 所以S3C2440总的寻址空间是1Gbyte。 市面…

java方法有excel实现_Java实现EXCEL操作(1)

Java实现EXCEL操作(1)1、实现方法&#xff1a;现在有三种方法去实现&#xff1a;jxl 、poi 、 FastExcel&#xff1a;97~2003在这里只讲poi实现方法。poi的包可以去Apache官网上去下载&#xff1a;http://poi.apache.org/download.html2、poi实现【1】低版本的导入导出方法&…

maven生成javadoc【原创】

1.命令模式&#xff1a; mvn javadoc:javadoc 2.eclipse下&#xff1a; 转载于:https://www.cnblogs.com/caiyuanzai/archive/2012/03/30/2425780.html

S3C2440_MMU

MMU,全称Memory Manage Unit, 中文名——存储器管理单元。 许多年以前&#xff0c;当人们还在使用DOS或是更古老的操作系统的时候&#xff0c;计算机的内存还非常小&#xff0c;一般都是以K为单位进行计算&#xff0c;相应的&#xff0c;当时的程序规模也不大&#xff0c;所以 …

某单位会java_Java核心API -- 4(日期类)

1. Date类(Java.utilDate)java.util.Date类用于封装日期及时间信息&#xff0c;一般仅用它显示某个日期&#xff0c;不对他作任何操作处理&#xff0c;作处理用Calendar类&#xff0c;计算方便。//创建一个Date实例&#xff0c;默认的构造方法创建的日期代表当前系统时间Date d…

java阴阳师抽卡概率_《阴阳师》公布抽卡概率!看到数字我哭了

随着《文化部关于规范网络游戏运营加强事中事后监管工作的通知》(以下简称“通知”)的正式生效&#xff0c;网游与手游似乎也迎来了一个全新的时代&#xff0c;除了我们之前关注的游戏帐号实名制认证之外&#xff0c;道具的合成以及氪金抽卡概率问题也非常值得玩家注意&#xf…

JAVA中返回值为字母时_LeetCode#524通过删除字母匹配到字典里最长单词-java中CompareTo方法用法以及Comparator中Compare方法返回值...

import java.util.Collections;import java.util.Comparator;import java.util.List;/*524. 通过删除字母匹配到字典里最长单词给定一个字符串和一个字符串字典&#xff0c;找到字典里面最长的字符串&#xff0c;该字符串可以通过删除给定字符串的某些字符来得到。如果答案不止…

关于JS中的constructor与prototype

在学习JS的面向对象过程中&#xff0c;一直对constructor与prototype感到很迷惑&#xff0c;看了一些博客与书籍&#xff0c;觉得自己弄明白了&#xff0c;现在记录如下&#xff1a; 我们都知道&#xff0c;在JS中有一个function的东西。一般人们叫它函数。比如下面的代码&…

resolv.conf

文件/etc/resolv.conf配置DNS客户&#xff0c;它包含了主机的域名搜索顺序和DNS服务器的地址&#xff0c;每一行应包含一个关键字和一个或多个的由空格隔开的参数。下面是一个例子文件&#xff1a; search mydom.edu.cnnameserver 210.34.0.14nameserver 210.34.0.2合法的参数及…

C# 温故而知新:Stream篇(六)

C# 温故而知新&#xff1a;Stream篇&#xff08;六&#xff09; BufferedStream 目录&#xff1a; 简单介绍一下BufferedStream如何理解缓冲区&#xff1f;BufferedStream的优势从BufferedStream 中学习装饰模式    如何理解装饰模式    再次理解下装饰模式在Stream中的…

HDU 3306 Another kind of Fibonacci

题意&#xff1a;A(0) 1 , A(1) 1 , A(N) X * A(N - 1) Y * A(N - 2) (N > 2)&#xff1b;给定三个值N&#xff0c;X&#xff0c;Y求S(N):S(N) A(0)2 A(1)2……A(n)2。 思路&#xff1a;原来我们讲的斐波那契数列是&#xff1a; F(0) 1, F(1) 1, F(N) F(N - 1) F(N…

Arm Linux交叉编译和连接过程分析(1)

一、配置内核&#xff08;Kconfig&#xff09; 我们配置内核是实质是根据众多目录下面的Kconfig文件中组合成我们需要的一个最佳选择&#xff0c;即最终在根目录下面生成的.config文件&#xff0c;而这个文件会在根目录Makefile下调用的。这一部分我们主要讨论整个SEP4020体系…

Arm Linux交叉编译和连接过程分析(2)

二、编译内核镜像过程 1、编译过程中涉及到到文件&#xff1a; /Makefile 编译产生顶层vmlinux镜像文件/scripts/Kbuild.include make过程中到一些基本定义 /scripts/Makefile.lib 编译内核时用到到函数库文件 /scripts/Makefile.build 内核编译到相关命令文件…

sql server 2008学习3 表组织和索引组织

表组织 表包含在一个或多个分区中&#xff0c;每个分区在一个堆或一个聚集索引结构包含数据行。堆页或聚集索引页在一个或多个分配单元中进行管理&#xff0c;具体的分配单元数取决于数据行中的列类型。 聚集表、堆和索引 SQL Server 表使用下列两种方法之一来组织其分区中的数…

Oracle备份standby,Oracle 11g 利用泠备份恢复standby库

Oracle 11g 利用泠备份恢复standby库1 开始在备库上进行泠备份先查好控制文件、redo、undo文件、数据文件的路径1.1 先关闭主库的归档日志传输SQL> ALTER system SETlog_archive_dest_state_2 DEFER;System altered.SQL>1.2 先关闭standby库SQL> shutdown immediate;D…