作者说是危险操作,实在看不清楚,感觉没有问题啊,还请高人指点!
/*
* Update: 2012-11-26
* WARNING
* the circular fifo used in “circularfifo_hazard_platform_dependent.hpp”
* is EXTREMELY platform dependent and should never be used except for testing
* purposes. For all intents and purposes THIS “circlarfifo_hazard” should be
* considered BROKEN
*
*
*
*
*
* Not any company”s property but Public-Domain
* Do with source-code as you will. No requirement to keep this
* header if need to use it/change it/ or do whatever with it
*
* Note that there is No guarantee that this code will work. And is actually
* proven to FAIL in the unit-test. I take no responsibility for this
* code and any problems you might get if using it.
* The code is HIGHLY PLATFORM DEPENDENT!
*
* Code & platform dependent issues with it was originally
* published at http://www.kjellkod.cc/threadsafecircularqueue
* 2009-11-02
* @author Kjell Hedstr鰉, hedstrom@kjellkod.cc */
#ifndef CIRCULARFIFO_HAZARD_H_
#define CIRCULARFIFO_HAZARD_H_
namespace hazard_by_convention {
/** Circular Fifo (a.k.a. Circular Buffer)
* Thread safe for one reader, and one writer */
template<typename Element, unsigned int Size>
class CircularFifo {
public:
enum {Capacity = Size+1};
CircularFifo() : tail(0), head(0){}
virtual ~CircularFifo() {}
bool push(const Element& item_);
bool pop(Element& item_);
bool wasEmpty() const;
bool wasFull() const;
private:
volatile unsigned int tail; // input index
Element array[Capacity];
volatile unsigned int head; // output index
unsigned int increment(unsigned int idx_) const;
// not allowed to copy nor assign
CircularFifo(const CircularFifo&);
void operator=(const CircularFifo&);
};
/** Producer only: Adds item to the circular queue.
* If queue is full at “push” operation no update/overwrite
* will happen, it is up to the caller to handle this case
*
* \param item_ copy by reference the input item
* \return whether operation was successful or not */
template<typename Element, unsigned int Size>
bool CircularFifo<Element, Size>::push(const Element& item_)
{
auto nextTail = increment(tail);
if(nextTail != head)
{
array[tail] = item_;
tail = nextTail;
return true;
}
// queue was full
return false;
}
/** Consumer only: Removes and returns item from the queue
* If queue is empty at “pop” operation no retrieve will happen
* It is up to the caller to handle this case
*
* \param item_ return by reference the wanted item
* \return whether operation was successful or not */
template<typename Element, unsigned int Size>
bool CircularFifo<Element, Size>::pop(Element& item_)
{
if(head == tail)
return false; // empty queue
item_ = array[head];
head = increment(head);
return true;
}
/** Useful for testinng and Consumer check of status
* Remember that the “empty” status can change quickly
* as the Procuder adds more items.
*
* \return true if circular buffer is empty */
template<typename Element, unsigned int Size>
bool CircularFifo<Element, Size>::wasEmpty() const
{
return (head == tail);
}
/** Useful for testing and Producer check of status
* Remember that the “full” status can change quickly
* as the Consumer catches up.
*
* \return true if circular buffer is full. */
template<typename Element, unsigned int Size>
bool CircularFifo<Element, Size>::wasFull() const
{
auto tailCheck = (tail+1) % Capacity;
return (tailCheck == head);
}
/** Increment helper function for index of the circular queue
* index is inremented or wrapped
*
* \param idx_ the index to the incremented/wrapped
* \return new value for the index */
template<typename Element, unsigned int Size>
unsigned int CircularFifo<Element, Size>::increment(unsigned int idx_) const
{
// increment or wrap
// =================
// index++;
// if(index == array.lenght) -> index = 0;
//
//or as written below:
// index = (index+1) % array.length
idx_ = (idx_+1) % Capacity;
return idx_;
}
} // hazard_by_convention
#endif /* CIRCULARFIFO_HAZARD_H_ */
/*
* Update: 2012-11-26
* WARNING
* the circular fifo used in “circularfifo_hazard_platform_dependent.hpp”
* is EXTREMELY platform dependent and should never be used except for testing
* purposes. For all intents and purposes THIS “circlarfifo_hazard” should be
* considered BROKEN
*
*
*
*
*
* Not any company”s property but Public-Domain
* Do with source-code as you will. No requirement to keep this
* header if need to use it/change it/ or do whatever with it
*
* Note that there is No guarantee that this code will work. And is actually
* proven to FAIL in the unit-test. I take no responsibility for this
* code and any problems you might get if using it.
* The code is HIGHLY PLATFORM DEPENDENT!
*
* Code & platform dependent issues with it was originally
* published at http://www.kjellkod.cc/threadsafecircularqueue
* 2009-11-02
* @author Kjell Hedstr鰉, hedstrom@kjellkod.cc */
#ifndef CIRCULARFIFO_HAZARD_H_
#define CIRCULARFIFO_HAZARD_H_
namespace hazard_by_convention {
/** Circular Fifo (a.k.a. Circular Buffer)
* Thread safe for one reader, and one writer */
template<typename Element, unsigned int Size>
class CircularFifo {
public:
enum {Capacity = Size+1};
CircularFifo() : tail(0), head(0){}
virtual ~CircularFifo() {}
bool push(const Element& item_);
bool pop(Element& item_);
bool wasEmpty() const;
bool wasFull() const;
private:
volatile unsigned int tail; // input index
Element array[Capacity];
volatile unsigned int head; // output index
unsigned int increment(unsigned int idx_) const;
// not allowed to copy nor assign
CircularFifo(const CircularFifo&);
void operator=(const CircularFifo&);
};
/** Producer only: Adds item to the circular queue.
* If queue is full at “push” operation no update/overwrite
* will happen, it is up to the caller to handle this case
*
* \param item_ copy by reference the input item
* \return whether operation was successful or not */
template<typename Element, unsigned int Size>
bool CircularFifo<Element, Size>::push(const Element& item_)
{
auto nextTail = increment(tail);
if(nextTail != head)
{
array[tail] = item_;
tail = nextTail;
return true;
}
// queue was full
return false;
}
/** Consumer only: Removes and returns item from the queue
* If queue is empty at “pop” operation no retrieve will happen
* It is up to the caller to handle this case
*
* \param item_ return by reference the wanted item
* \return whether operation was successful or not */
template<typename Element, unsigned int Size>
bool CircularFifo<Element, Size>::pop(Element& item_)
{
if(head == tail)
return false; // empty queue
item_ = array[head];
head = increment(head);
return true;
}
/** Useful for testinng and Consumer check of status
* Remember that the “empty” status can change quickly
* as the Procuder adds more items.
*
* \return true if circular buffer is empty */
template<typename Element, unsigned int Size>
bool CircularFifo<Element, Size>::wasEmpty() const
{
return (head == tail);
}
/** Useful for testing and Producer check of status
* Remember that the “full” status can change quickly
* as the Consumer catches up.
*
* \return true if circular buffer is full. */
template<typename Element, unsigned int Size>
bool CircularFifo<Element, Size>::wasFull() const
{
auto tailCheck = (tail+1) % Capacity;
return (tailCheck == head);
}
/** Increment helper function for index of the circular queue
* index is inremented or wrapped
*
* \param idx_ the index to the incremented/wrapped
* \return new value for the index */
template<typename Element, unsigned int Size>
unsigned int CircularFifo<Element, Size>::increment(unsigned int idx_) const
{
// increment or wrap
// =================
// index++;
// if(index == array.lenght) -> index = 0;
//
//or as written below:
// index = (index+1) % array.length
idx_ = (idx_+1) % Capacity;
return idx_;
}
} // hazard_by_convention
#endif /* CIRCULARFIFO_HAZARD_H_ */
解决方案
20
看了半天,觉得作者的意思是这部分代码很依赖于平台,因此,可能在他本人的平台上没问题,但是在别的平台上进行单元测试的时候可能由于架构设计不同等问题会出现失败。同时作者说了,不要用于商业目的,出了问题不要找他,假如是用于公共领域或测试的目的,随便你怎么改。至于说是危险动作,本人没看出来啊。
60
具体说就是例如两个线程假如不在一个核上,那么一个线程修改内存另一个线程可能看不到。这种时候需要特殊的机制让内存的修改在另一个核上可见。这也就是为什么只用volatile解决不了多线程问题。
平台相关本人估计他想说的是VC有个约等于bug的问题就是volatile会导致修改在别的核上可见。
平台相关本人估计他想说的是VC有个约等于bug的问题就是volatile会导致修改在别的核上可见。
5
你是说一个线程读,一个线程写吧,volatile是不能防止冲突的,推荐使用原子变量
5
voliate在c++里面作用有限,防止编译器做一些优化。假如不是c++11那就枷锁,假如是c++11那么就用原子变量。
5
仅供参考:
//循环向a函数每次发送200个字节长度(这个是固定的)的buffer, //a函数中需要将循环传进来的buffer,组成240字节(也是固定的)的新buffer进行处理, //在处理的时候每次从新buffer中取两个字节打印 #ifdef _MSC_VER #pragma warning(disable:4996) #endif #include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef _MSC_VER #include <windows.h> #include <process.h> #include <io.h> #define MYVOID void #define vsnprintf _vsnprintf #else #include <unistd.h> #include <sys/time.h> #include <pthread.h> #define CRITICAL_SECTION pthread_mutex_t #define MYVOID void * #endif //Log{ #define MAXLOGSIZE 20000000 #define MAXLINSIZE 16000 #include <time.h> #include <sys/timeb.h> #include <stdarg.h> char logfilename1[]="MyLog1.log"; char logfilename2[]="MyLog2.log"; static char logstr[MAXLINSIZE+1]; char datestr[16]; char timestr[16]; char mss[4]; CRITICAL_SECTION cs_log; FILE *flog; #ifdef _MSC_VER void Lock(CRITICAL_SECTION *l) { EnterCriticalSection(l); } void Unlock(CRITICAL_SECTION *l) { LeaveCriticalSection(l); } void sleep_ms(int ms) { Sleep(ms); } #else void Lock(CRITICAL_SECTION *l) { pthread_mutex_lock(l); } void Unlock(CRITICAL_SECTION *l) { pthread_mutex_unlock(l); } void sleep_ms(int ms) { usleep(ms*1000); } #endif void LogV(const char *pszFmt,va_list argp) { struct tm *now; struct timeb tb; if (NULL==pszFmt||0==pszFmt[0]) return; vsnprintf(logstr,MAXLINSIZE,pszFmt,argp); ftime(&tb); now=localtime(&tb.time); sprintf(datestr,"%04d-%02d-%02d",now->tm_year+1900,now->tm_mon+1,now->tm_mday); sprintf(timestr,"%02d:%02d:%02d",now->tm_hour ,now->tm_min ,now->tm_sec ); sprintf(mss,"%03d",tb.millitm); printf("%s %s.%s %s",datestr,timestr,mss,logstr); flog=fopen(logfilename1,"a"); if (NULL!=flog) { fprintf(flog,"%s %s.%s %s",datestr,timestr,mss,logstr); if (ftell(flog)>MAXLOGSIZE) { fclose(flog); if (rename(logfilename1,logfilename2)) { remove(logfilename2); rename(logfilename1,logfilename2); } } else { fclose(flog); } } } void Log(const char *pszFmt,...) { va_list argp; Lock(&cs_log); va_start(argp,pszFmt); LogV(pszFmt,argp); va_end(argp); Unlock(&cs_log); } //Log} #define ASIZE 200 #define BSIZE 240 #define CSIZE 2 char Abuf[ASIZE]; char Cbuf[CSIZE]; CRITICAL_SECTION cs_HEX; CRITICAL_SECTION cs_BBB; struct FIFO_BUFFER { int head; int tail; int size; char data[BSIZE]; } BBB; int No_Loop=0; void HexDump(int cn,char *buf,int len) { int i,j,k; char binstr[80]; Lock(&cs_HEX); for (i=0;i<len;i++) { if (0==(i%16)) { sprintf(binstr,"%03d %04x -",cn,i); sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]); } else if (15==(i%16)) { sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]); sprintf(binstr,"%s ",binstr); for (j=i-15;j<=i;j++) { sprintf(binstr,"%s%c",binstr,("!"<buf[j]&&buf[j]<="~")?buf[j]:"."); } Log("%s\n",binstr); } else { sprintf(binstr,"%s %02x",binstr,(unsigned char)buf[i]); } } if (0!=(i%16)) { k=16-(i%16); for (j=0;j<k;j++) { sprintf(binstr,"%s ",binstr); } sprintf(binstr,"%s ",binstr); k=16-k; for (j=i-k;j<i;j++) { sprintf(binstr,"%s%c",binstr,("!"<buf[j]&&buf[j]<="~")?buf[j]:"."); } Log("%s\n",binstr); } Unlock(&cs_HEX); } int GetFromRBuf(int cn,CRITICAL_SECTION *cs,struct FIFO_BUFFER *fbuf,char *buf,int len) { int lent,len1,len2; lent=0; Lock(cs); if (fbuf->size>=len) { lent=len; if (fbuf->head+lent>BSIZE) { len1=BSIZE-fbuf->head; memcpy(buf ,fbuf->data+fbuf->head,len1); len2=lent-len1; memcpy(buf+len1,fbuf->data ,len2); fbuf->head=len2; } else { memcpy(buf ,fbuf->data+fbuf->head,lent); fbuf->head+=lent; } fbuf->size-=lent; } Unlock(cs); return lent; } MYVOID thdB(void *pcn) { char *recv_buf; int recv_nbytes; int cn; int wc; int pb; cn=(int)pcn; Log("%03d thdB thread begin...\n",cn); while (1) { sleep_ms(10); recv_buf=(char *)Cbuf; recv_nbytes=CSIZE; wc=0; while (1) { pb=GetFromRBuf(cn,&cs_BBB,&BBB,recv_buf,recv_nbytes); if (pb) { Log("%03d recv %d bytes\n",cn,pb); HexDump(cn,recv_buf,pb); sleep_ms(1); } else { sleep_ms(1000); } if (No_Loop) break;// wc++; if (wc>3600) Log("%03d %d==wc>3600!\n",cn,wc); } if (No_Loop) break;// } #ifndef _MSC_VER pthread_exit(NULL); #endif } int PutToRBuf(int cn,CRITICAL_SECTION *cs,struct FIFO_BUFFER *fbuf,char *buf,int len) { int lent,len1,len2; Lock(cs); lent=len; if (fbuf->size+lent>BSIZE) { lent=BSIZE-fbuf->size; } if (fbuf->tail+lent>BSIZE) { len1=BSIZE-fbuf->tail; memcpy(fbuf->data+fbuf->tail,buf ,len1); len2=lent-len1; memcpy(fbuf->data ,buf+len1,len2); fbuf->tail=len2; } else { memcpy(fbuf->data+fbuf->tail,buf ,lent); fbuf->tail+=lent; } fbuf->size+=lent; Unlock(cs); return lent; } MYVOID thdA(void *pcn) { char *send_buf; int send_nbytes; int cn; int wc; int a; int pa; cn=(int)pcn; Log("%03d thdA thread begin...\n",cn); a=0; while (1) { sleep_ms(100); memset(Abuf,a,ASIZE); a=(a+1)%256; if (16==a) {No_Loop=1;break;}//去掉这句可以让程序一直循环直到按Ctrl+C或Ctrl+Break或当前目录下存在文件No_Loop send_buf=(char *)Abuf; send_nbytes=ASIZE; Log("%03d sending %d bytes\n",cn,send_nbytes); HexDump(cn,send_buf,send_nbytes); wc=0; while (1) { pa=PutToRBuf(cn,&cs_BBB,&BBB,send_buf,send_nbytes); Log("%03d sent %d bytes\n",cn,pa); HexDump(cn,send_buf,pa); send_buf+=pa; send_nbytes-=pa; if (send_nbytes<=0) break;// sleep_ms(1000); if (No_Loop) break;// wc++; if (wc>3600) Log("%03d %d==wc>3600!\n",cn,wc); } if (No_Loop) break;// } #ifndef _MSC_VER pthread_exit(NULL); #endif } int main() { #ifdef _MSC_VER InitializeCriticalSection(&cs_log); InitializeCriticalSection(&cs_HEX); InitializeCriticalSection(&cs_BBB); #else pthread_t threads[2]; int threadsN; int rc; pthread_mutex_init(&cs_log,NULL); pthread_mutex_init(&cs_HEX,NULL); pthread_mutex_init(&cs_BBB,NULL); #endif Log("Start===========================================================\n"); BBB.head=0; BBB.tail=0; BBB.size=0; #ifdef _MSC_VER _beginthread((void(__cdecl *)(void *))thdA,0,(void *)1); _beginthread((void(__cdecl *)(void *))thdB,0,(void *)2); #else threadsN=0; rc=pthread_create(&(threads[threadsN++]),NULL,thdA,(void *)1);if (rc) Log("%d=pthread_create %d error!\n",rc,threadsN-1); rc=pthread_create(&(threads[threadsN++]),NULL,thdB,(void *)2);if (rc) Log("%d=pthread_create %d error!\n",rc,threadsN-1); #endif if (!access("No_Loop",0)) { remove("No_Loop"); if (!access("No_Loop",0)) { No_Loop=1; } } while (1) { sleep_ms(1000); if (No_Loop) break;// if (!access("No_Loop",0)) { No_Loop=1; } } sleep_ms(3000); Log("End=============================================================\n"); #ifdef _MSC_VER DeleteCriticalSection(&cs_BBB); DeleteCriticalSection(&cs_HEX); DeleteCriticalSection(&cs_log); #else pthread_mutex_destroy(&cs_BBB); pthread_mutex_destroy(&cs_HEX); pthread_mutex_destroy(&cs_log); #endif return 0; }
任何收发两端速度不一致的通讯,都需要在它们之间使用一个足够大的FIFO缓冲区。
对任何FIFO缓冲区的使用,都需要仔细考虑接收端接收时超时无数据和发送端发送时FIFO缓冲区已满这两种情况下该怎么样做。
这些概念都在这段经典代码中有所体现。
这段经典代码还包括以下必须考虑的因素:
◆跨Windows和Linux平台
◆多线程锁
◆多线程日志
◆日志文件占用的磁盘空间可控
◆日志中的时间包括毫秒
◆传输的数据对应的每个字节到底是几
◆怎么样退出多线程程序
◆……
5
好多东西都忘了… 粗略看了下貌似x86处理器同一CPU内是能保证写入可见的。但是本人不确定是不是全部内存模式都行,感觉write-combining模式核内都需要memory ordering才能保证可见。
另外无序执行的x86处理器,假如不用memory ordering,写入顺序依然不能保证。即便编译器不给调整顺序,CPU执行的顺序依然可能是反的。
intel多CPU环境本人有点犯晕,然而没时间细琢磨这事了。
而以上这些全都平台相关。
另外无序执行的x86处理器,假如不用memory ordering,写入顺序依然不能保证。即便编译器不给调整顺序,CPU执行的顺序依然可能是反的。
intel多CPU环境本人有点犯晕,然而没时间细琢磨这事了。
而以上这些全都平台相关。