摘自:http://www.oschina.net/code/snippet_54334_12505
代码源于 http://www.ibm.com/developerworks/cn/linux/l-cn-lockfree/ 的实现.
注意: 构造时参数 buf_size 必须是2的N次方.
#ifndef min #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif #ifndef max #define max(a,b) (((a) > (b)) ? (a) : (b)) #endif // 无锁缓冲队列. class circular_buffer { public: circular_buffer(int buf_size) : m_buffer_size(buf_size) , m_circle_buffer(NULL) , m_write_p(0) , m_read_p(0) { m_circle_buffer = new char[m_buffer_size]; } ~circular_buffer() { if (m_circle_buffer) delete[] m_circle_buffer; m_circle_buffer = NULL; } void clear() { m_write_p = 0; m_read_p = 0; } unsigned int available() { return m_buffer_size - (m_write_p - m_read_p); } unsigned int used() { return m_write_p - m_read_p; } unsigned int put_data(char* buffer, unsigned int len) { unsigned int l; len = _min(len, m_buffer_size - m_write_p + m_read_p); /* first put the data starting from fifo->in to buffer end */ l = _min(len, m_buffer_size - (m_write_p & (m_buffer_size - 1))); memcpy(m_circle_buffer + (m_write_p & (m_buffer_size - 1)), buffer, l); /* then put the rest (if any) at the beginning of the buffer */ memcpy(m_circle_buffer, buffer + l, len - l); m_write_p += len; return len; } unsigned int get_data(char* buffer, unsigned int len) { unsigned int l; len = _min(len, m_write_p - m_read_p); /* first get the data from fifo->out until the end of the buffer */ l = _min(len, m_buffer_size - (m_read_p & (m_buffer_size - 1))); memcpy(buffer, m_circle_buffer + (m_read_p & (m_buffer_size - 1)), l); /* then get the rest (if any) from the beginning of the buffer */ memcpy(buffer + l, m_circle_buffer, len - l); m_read_p += len; return len; } protected: inline unsigned int _max(unsigned int a, unsigned int b) { return max(a, b); } inline unsigned int _min(unsigned int a, unsigned int b) { return min(a, b); } private: int m_buffer_size; char* m_circle_buffer; unsigned int m_write_p; unsigned int m_read_p; };
摘自:http://blog.csdn.net/xocoder/article/details/7880769
最近在重构之前写的网络底层时,从各个方面认真考虑了每一个细节实现。其中,在提交I/O(WSASend/WSARecv)和I/O完成(GetQueuedCompletionStatus)时,难免出现一个缓冲区需要两个线程公用的问题。
假设主线程不断发送该消息,这些消息被堆叠在一个缓冲区里,定时使用WSASend提交发送I/O请求,在GetQueuedCompletionStatus返回后,才能按照已发送的字节数去删掉该缓冲区里相应字节数的数据。不明白?好吧我说的简单一些。
WSASend调用后,你传递的参数只是说明:我希望发送这么多数据。但请求提交后,你的要求未必能够被全部满足,也就是说也许你想发送1024字节的东西,但也许GetQueuedCompletionStatus返回,操作完成后,本次只成功发送了1000个字节,也就是说剩余的24字节的数据,你还需要再调用WSASend,直到都发送成功为止。所以在这种情况下,一定要等待GetQueuedCompletionStatus返回,才知道究竟发送成功了多少,也才能从之前的发送缓冲里删掉数据。否则,如果你在提交WSASend时就把数据删掉了,而GetQueuedCompletionStatus返回后却告诉你只发了1000字节,那就杯具了-----那24字节的数据永远地离开了我们。
而在这种情况下,我们发送消息,也就是向这个缓冲区后面堆放要发送的数据,是主线程中执行的,而GetQueuedCompletionStatus完成后,从缓冲区内弹出数据,确是IOCP的工作线程做的。当然,最简单的办法就是------加个锁呗。但是,在I/O频繁的情况下,可以想象会出现多少线程争用的情况。于是,就有了本文要说的东西:环形缓冲。
环形缓冲的原理并不难理解,只适用于一个线程写,一个线程读的情况。环形缓冲的原理我就不再赘述,可以自行搜索。
废话不多说。下面给出我在这次优化中写的一个环形缓冲类,该环形缓冲完美地在IOCP中工作了起来,实实在在地解决了线程争用引发地效率低下。
2012.9.1 0:57 重贴代码
修改了一个可能出现的误置Full标志的BUG,之前的代码中,是先增加写指针,再判断是否等于读指针,等于则置Full标志,但若在该判断之前,读线程将数据读空,此时写线程继续工作,进行该判断时,就会发现写指针 = 读指针(但是是由于读空造成的),于是错误地将状态置为Full。
#pragma once #include "XBaseDefine.h" enum eXRingBufferState { eXRingBufferState_None = 0, eXRingBufferState_Empty = 1, eXRingBufferState_Full = 2, eXRingBufferState_Max }; class XRingBuffer { public: XRingBuffer(const DWORD size); ~XRingBuffer(); bool pushData(const char* data, const DWORD size); bool copyData(char* dest, const DWORD destSize, const DWORD size); bool popData(char* dest, const DWORD destSize, const DWORD popSize); bool popData(const DWORD popSize); const DWORD getUsedSize() const; const DWORD getFreeSize() const; private: DWORD _size; char* _buffer; volatile DWORD _write_pos; volatile DWORD _read_pos; volatile DWORD _state; };
#include "XRingBuffer.h" XRingBuffer::XRingBuffer(const DWORD size) : _size(size), _buffer(NULL), _write_pos(0), _read_pos(0), _state(eXRingBufferState_Empty) { _buffer = new char[size]; } XRingBuffer::~XRingBuffer() { delete [] _buffer; _buffer = NULL; } bool XRingBuffer::pushData(const char* data, const DWORD size) { // 一定要先memcpy再设置writePos,读取也一样,要先有数据,才设置标志 const DWORD freeSize = getFreeSize(); if(size > freeSize) { return false; } // 此处一定要先置state,如果把置state放在写指针增加完成后,有可能出现在置state之前,数据被读取空了, // 写指针和读指针相等,虽然读空时把state置为了Empty,但紧接着此处又错误地置为Full了。 if(size == freeSize) { _state = eXRingBufferState_Full; } if(_write_pos < _read_pos) { assert(_read_pos - _write_pos >= size); memcpy_s(_buffer + _write_pos, _read_pos - _write_pos, data, size); _write_pos += size; } else { if(_write_pos + size > _size) { // 尾部溢出 // 先拷贝一部分长度到尾部,然后在从头开始拷贝 DWORD wSize = _size - _write_pos; memcpy_s(_buffer + _write_pos, wSize, data, wSize); // 拷贝剩余部分到头部 memcpy_s(_buffer, freeSize - wSize, data + wSize, size - wSize); _write_pos = size - wSize; } else { assert(_size - _write_pos >= size); memcpy_s(_buffer + _write_pos, freeSize, data, size); _write_pos = (_write_pos + size) % _size; } } return true; } bool XRingBuffer::copyData(char* dest, const DWORD destSize, const DWORD size) { if(size > getUsedSize()) { return false; } if(_read_pos + size > _size) { // 到尾部还不够,需要再从头读 // 先把尾部的拷进去 DWORD wSize = _size - _read_pos; memcpy_s(dest, destSize, _buffer + _read_pos, wSize); // 拷贝头部的 memcpy_s(dest + wSize, destSize - wSize, _buffer, size - wSize); } else { memcpy_s(dest, destSize, _buffer + _read_pos, size); } return true; } bool XRingBuffer::popData(char* dest, const DWORD destSize, const DWORD popSize) { const DWORD usedSize = getUsedSize(); if(popSize > usedSize) { return false; } // 此处一定要先置state,原因参考pushData注释 if(usedSize == popSize) { _state = eXRingBufferState_Empty; } if(_read_pos + popSize > _size) { // 到尾部还不够,需要再从头读 // 先把尾部的拷进去 DWORD wSize = _size - _read_pos; memcpy_s(dest, destSize, _buffer + _read_pos, wSize); // 拷贝头部的 memcpy_s(dest + wSize, destSize - wSize, _buffer, popSize - wSize); _read_pos = popSize - wSize; } else { memcpy_s(dest, destSize, _buffer + _read_pos, popSize); _read_pos = (_read_pos + popSize) % _size; } return true; } bool XRingBuffer::popData(const DWORD popSize) { const DWORD usedSize = getUsedSize(); if(popSize > usedSize) { return false; } // 此处一定要先置state,原因参考pushData注释 if(usedSize == popSize) { _state = eXRingBufferState_Empty; } if(_read_pos + popSize > _size) { // 到尾部还不够,需要再从头读 DWORD wSize = _size - _read_pos; _read_pos = popSize - wSize; } else { _read_pos = (_read_pos + popSize) % _size; } return true; } const DWORD XRingBuffer::getUsedSize() const { return _size - getFreeSize(); } const DWORD XRingBuffer::getFreeSize() const { if(_write_pos == _read_pos) { if(_state == eXRingBufferState_Empty) { return _size; } else { return 0; } } else if(_write_pos < _read_pos) { return _read_pos - _write_pos; } else { return _size - (_write_pos - _read_pos); } }