标签:
原文链接:http://blog.csdn.net/billow_zhang/article/details/4420789
在程序的两个模块间进行通讯的时候,缓冲区成为一个经常使用的机制。
如上图,写入模块将信息写入缓冲区中,读出模块将信息读出缓冲区。这样使得:
缓冲区显然不适合下面的情况:
队列使用环形队列,如上图。环形队列的特点是,不需要进行动态的内存释放和分配,使用固定大小的内存空间反复使用。在实际的队列插入和弹出操作中, 是不断交叉进行的,当push操作时,head会增加;而pop操作时,tail会增加。push的速度快的时候,有可能追上 tail,这个时候说明队列已经满了,不能再进行push的操作了,需要等待 pop 操作腾出队列的空间。当 pop 的操作快,使得 tail 追上 head,这个时候说明队列已空了,不能再进行 pop 操作了,需要等待 push 进来数据。
下面列出了一个环形队列类的的数据结构的源程序 。
程序里定义了两个类 LoopQue_impl及LoopQueue。前者定义了环形队列的基本数据结构和实现,后者又进行了一次内存分配包装。
21行的LoopQue_impl的构造函数是以队列的空间大小作为参数创建这个类的。也就是说,在类创建的时候,就决定了队列的大小。
63行中定义的队列的数组空间为 _Tp data[0]。这似乎是一个奇怪的事情。事实上,这个空间大小应该是max_size个数组空间。 但由于max_size是在类创建的时候确定的,在这里,data[0]只起到一个占位符的作用。所以,LoopQue_impl这个类是不能直接使用的, 需要正确的分配好内存大小,才能使用,这也是需要里另外设计一个类LoopQueue的重要原因之一。也许您会奇怪,为什么要这样使用呢?如果定义一个指针, 例如:_Tp *data,然后在构造函数里面使用 data = new _Tp[max_size],不是很容易吗?但是,不要忘记了,我们这个环形队列类有可能会是一个进程间共享类。 例如,一个进程push操作,另一个进程pop操作。这样,这个类是需要建立在共享内存中的。而共享内存中的类的成员,如果包含有指针或者引用这样的类型, 将给内存分配带来很大的麻烦。而我们这样以这个占位符的方式设计这个类,将减少这种麻烦和复杂性。
17行的addsize的类函数确定了LoopQue_impl需要的另外的内存空间的大小。
LoopQueue显示了怎样使用LoopQue_impl,解决内存分配问题的。从79行的模版参数中,我们看到,除了缓冲区数据存放类型_Tp的参数外,还有一个Alloc类型。 这便是用于分配LoopQue_impl内存空间使用的模版类。
在LoopQueue的成员中,定义了LoopQue_impl的一个引用 impl(102行)。这个引用便是指向使用Alloc分配空间得来的LoopQue_impl的空间。
Alloc模版参数有一个缺省的定义值 LoopQue_allocate。从这个缺省的分配内存的类里,我们可以看到一个分配LoopQue_impl的实现样例,见69行:
char *p = new char[sizeof(LoopQue_impl<_Tp>) + LoopQue_impl<_Tp>::addsize(msize)];
return *(new (p) LoopQue_impl<_Tp>(msize));
这里,先根据msize分配好了靠虑到了data[msize]的足够的内存,然后,再使用定位的new操作,将LoopQue_impl创建在这个内存区中。这样,LoopQue_impl类就可以使用它其中的 _Tp data[0]的成员。实际上,这个成员已经有 _Tp data[msize]这样的空间了。 这里,如果我们设计另外一个分配内存的类,例如,LoopQue_ShmAlloc。这个类是使用共享内存,并在其中建立LoopQue_impl类。这样我们就可以使用:
LoopQue<_Tp, LoopQue_ShmAlloc>
来创建一个可以在进程间共享而进行通讯的环形队列类了。
这些工作我们将在随后的设计中进行。且听下回分解。
附件: LoopQue的测试程序:
标签:
原文地址:http://www.cnblogs.com/codecamel/p/4701889.html