一个生产者多消费者的应用 , 代码里目前就先简单用了单生成单消费,
每一局麻将都需要记录:每一次的发牌,抓牌,出牌 ,吃碰杠胡听;
把所有的牌 序列化成二进制存入文件
每一局麻将生成一个key , 可随意自己组合
下面代码思路仅供参考, 还未测试过; 可修改成多线程日志
主线程唯一需要在开始的时候调用一次 start , 记录用push;
2种模式可选, 一种不关闭文件handle, 直到一局游戏结束,再关闭
一种每次打开,写入,关闭;
2种写入方式, 一种写入2进制流 ,
一种把2进制流转成一个个unsigned long储存在文件
[ 14张牌从 0x01 ~ 0x48 ] 0x48需要 7 位,因此每张牌用7位来储存,最多 98 位,
对于一个unsigned long 最多储存 4 张牌, 因此有14张牌时需要 4个unsigned long
class ErrorInfo{
static const SIZE_T ERR_MSG_SIZE = 1 << 13;
static CRITICAL_SECTION err_cs;
static std::once_flag oneflag;
public:
static void init(){
std::call_once(ErrorInfo::oneflag,&ErrorInfo::init_cs);
}
static void init_cs(){
InitializeCriticalSectionAndSpinCount(&ErrorInfo::err_cs, 4000);
}
static std::string getError(DWORD err){
EnterCriticalSection(&ErrorInfo::err_cs);
static HANDLE g_heap = HeapCreate(0, ERR_MSG_SIZE, 0);
static char *buf = (char*)HeapAlloc(g_heap, 0, ERR_MSG_SIZE);
static std::string errstr;
static std::stringstream ss;
ss.str("");
ss.clear();
errstr.clear();
DWORD syslocale = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
DWORD ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err, syslocale, buf, ERR_MSG_SIZE, NULL);
if (!ret){
static HMODULE hDll = LoadLibraryEx(TEXT("netmsg.dll"), NULL, DONT_RESOLVE_DLL_REFERENCES);
if (hDll){
//如果在dll中查找,FORMAT_MESSAGE_FROM_HMODULE 添加上去, 第2个参数填写句柄
ret = FormatMessageA(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
hDll, err, syslocale, buf, ERR_MSG_SIZE, NULL);
}
}
if (ret && buf){
buf[ret] = 0;
errstr = buf;
LeaveCriticalSection(&ErrorInfo::err_cs);
return errstr;
}
ss << err;
ss >> errstr;
LeaveCriticalSection(&ErrorInfo::err_cs);
return errstr;
}
};
CRITICAL_SECTION ErrorInfo::err_cs;
std::once_flag ErrorInfo::oneflag;
#include <bitset>
struct FileHandleData{
HANDLE hFile;
volatile bool inUse;
std::string filepath;
FileHandleData(HANDLE file = INVALID_HANDLE_VALUE, bool inUse = false):hFile(file),inUse(inUse){}
};
struct storeData{
std::string key; //随意的key
std::string data; //数据
int type; //类型[抓牌,出牌,吃碰杠等]
int seat; //座位号
storeData(const char * key, const char *data , int nDataLen , int type, int seat):key(key),data(data,nDataLen) , type(type) , seat(seat){}
storeData(storeData && obj) throw() : key(std::move(obj.key)),data(std::move(obj.data)) , type(obj.type) , seat(obj.seat){}
storeData & operator=(storeData && obj) throw(){
if(this != &obj){
key = std::move(obj.key);
data = std::move(obj.data);
type = obj.type;
seat = obj.seat;
}
return *this;
}
};
struct restoreData{
int nLen;
int type;
int seat;
char handCards[14];
char buf[0];
};
class RecordFile
{
CRITICAL_SECTION write_cs;
CRITICAL_SECTION prepare_cs;
CRITICAL_SECTION errlog_cs;
CONDITION_VARIABLE write_cond;
HANDLE hSem;
deque<storeData> prepare_queue;
deque<std::string> wait_for_close_queue;
deque<storeData> write_queue;
std::unordered_map<std::string,FileHandleData> write_map;
HANDLE hThreadProducer;
unsigned tid_producer;
HANDLE hThreadConsumers[4];
unsigned tid_consumers[4];
bool bStopPrepare;
bool bStopProcess;
bool bAllDone;
bool bStarted;
int m_writeThreadCcount;
int m_operate;
int m_writeMode;
std::string cur_dir_name;
HANDLE hErrorLog;
std::string errorLogPath;
std::list<restoreData*> m_restoreList;
public:
enum eOperate { OPENFILE = 1, CLOSEFILE = 2};
enum eSpinCount { SpinCountMin = 1000, SpinCountNormal = 2000, SpinCountMax = 4000};
enum eSemaphoreCount{ SemMin = 200 , SemNormal = 1000 , SemMax = 2000};
enum eWriteMode{ WriteModeStream = 1, WriteModeLong = 2};
RecordFile(int operate =CLOSEFILE ,int writeMode = WriteModeStream, int writeThreadCount = 1):bAllDone(false),
bStarted(false),
bStopProcess(false),
bStopPrepare(false),
m_operate(operate),m_writeMode(writeMode),
m_writeThreadCcount(writeThreadCount)
{
cur_dir_name = std::string(typeid(*this).name()).substr(6);
errorLogPath = cur_dir_name + "/errorlog.log";
ErrorInfo::init();
}
void push(const char * key, const char * data , int nDataLen, int type , int seat){
EnterCriticalSection(&prepare_cs);
prepare_queue.emplace_back(key,data,nDataLen, type , seat);
LeaveCriticalSection(&prepare_cs);
ReleaseSemaphore(hSem,1,0);
}
void start(){
if(bStarted)
return;
if(!(OPENFILE == m_operate || CLOSEFILE == m_operate )){
return;
}
bStarted = true;
bAllDone = false;
bStopProcess = false;
bStopPrepare = false;
hThreadProducer = NULL;
memset(hThreadConsumers,0,sizeof(hThreadConsumers));
InitializeCriticalSectionAndSpinCount(&write_cs , SpinCountMax);
InitializeCriticalSectionAndSpinCount(&prepare_cs , SpinCountMax);
InitializeCriticalSectionAndSpinCount(&errlog_cs , SpinCountMax);
InitializeConditionVariable(&write_cond);
hSem = CreateSemaphoreW(0,0,SemMax,NULL);