64,637
社区成员
发帖
与我相关
我的任务
分享
#include<windows.h>
#include<fstream.h>
#include<stdio.h>
#include<string>
#include<conio.h>
//定义一些常量;
//本程序允许的最大临界区数;
#define MAX_BUFFER_NUM 10
//秒到微秒的乘法因子;
#define INTE_PER_SEC 1000
//本程序允许的生产和消费线程的总数;
#define MAX_THREAD_NUM 64
//定义一个结构,记录在测试文件中指定的每一个线程的参数
struct ThreadInfo
{
int serial; //线程序列号
char entity; //是P还是C /*生产者线程 或 消费者线程*/
double delay; //线程延迟 /*线程在创建后,多长时间正式开始执行*/
int thread_request[MAX_THREAD_NUM]; //线程请求队列 //请求消费哪些生产者线程的产品
int n_request; //请求个数
};
//全局变量的定义
//临界区对象的声明,用于管理缓冲区的互斥访问;
CRITICAL_SECTION PC_Critical[MAX_BUFFER_NUM]; //每个缓冲区设置一个临界区,用于消费者消费
int Buffer_Critical[MAX_BUFFER_NUM]; //缓冲区声明,用于存放产品;
/*Buffer_Critical数组元素的值如果为-1,表示该缓冲区为空
如果为-2,表示该缓冲区正在进行写操作
如果为大于等于0的数i,表示第i个生产者进程已在其中放入了产品
//意味着第i个生产者线程生产的产品的编号就是i,与线程号i保持一致
*/
HANDLE h_Thread[MAX_THREAD_NUM]; //用于存储每个线程句柄的数组;
ThreadInfo Thread_Info[MAX_THREAD_NUM]; //线程信息数组;
HANDLE empty_semaphore; //一个信号量;
HANDLE h_mutex; //一个互斥量;
DWORD n_Thread = 0; //实际的线程的数目;
DWORD n_Buffer_or_Critical; //实际的缓冲区或者临界区的数目;
HANDLE h_Semaphore[MAX_THREAD_NUM]; //生产者允许消费者开始消费的信号量;的数组
//生产者每生产出一种产品,就将该产品对应的h_Semaphore信号量值加n_Thread,允许多个进程依次消费该产品
//生产消费及辅助函数的声明
void Produce(void *p);
void Consume(void *p);
bool IfInOtherRequest(int);
int FindProducePositon();
int FindBufferPosition(int);
int main(void)
{
//声明所需变量;
DWORD wait_for_all;
ifstream inFile; //define an ifstream object
//初始化缓冲区;
for(int i=0;i< MAX_BUFFER_NUM;i++)
Buffer_Critical[i] = -1; //-1表未当前为空
//初始化每个线程的请求队列;
for(int j=0;j<MAX_THREAD_NUM;j++){
for(int k=0;k<MAX_THREAD_NUM;k++)
Thread_Info[j].thread_request[k] = -1; //thread_request[0]...thread_request[k]的值均为-1,不请求任何一个线程
Thread_Info[j].n_request = 0; //请求线程的个数为0
}
//初始化临界区;
for(i =0;i< MAX_BUFFER_NUM;i++)
InitializeCriticalSection(&PC_Critical[i]); //依次初始化每个缓冲区的临界区,临界区在开始使用前必须进行初始化操作
//打开输入文件,按照规定的格式提取线程等信息;
inFile.open("test.txt");
//从文件中获得实际的缓冲区的数目;
inFile >> n_Buffer_or_Critical; //文件的第一行数据是设定的实际的缓冲区数目
inFile.get();
printf("输入文件的内容如下:\n");
//回显获得的缓冲区的数目信息;
printf(" %d \n",(int) n_Buffer_or_Critical);
//提取每个线程的信息到相应数据结构中;
while(inFile){
inFile >> Thread_Info[n_Thread].serial; //依次读入线程的序号、线程类型、线程开始延迟时间
inFile >> Thread_Info[n_Thread].entity;
inFile >> Thread_Info[n_Thread].delay;
char c;
inFile.get(c);
while(c!='\n'&& !inFile.eof()){ //判断是不是该行已结束或已到文件尾,如果不是(也意味着这是一个消费者,还需读入它请求消费哪些生产者的产品)则...
inFile>> Thread_Info[n_Thread].thread_request[Thread_Info[n_Thread].n_request++]; //将请求的生产者进程的序号放入其thread_request数组中,并将其n_request加1计数
inFile.get(c);
}
n_Thread++;
}
//回显获得的线程信息,便于确认正确性;
for(j=0;j<(int) n_Thread;j++){
int Temp_serial = Thread_Info[j].serial;
char Temp_entity = Thread_Info[j].entity;
double Temp_delay = Thread_Info[j].delay;
printf(" \n thread%2d %c %f ",Temp_serial,Temp_entity,Temp_delay);//这是打印消费者和生产者进程所共有三项信息
int Temp_request = Thread_Info[j].n_request;//如果线程的n_request>0,说明这是一个消费者线程,存在消费请求,将这些信息也输出
for(int k=0;k<Temp_request;k++)
printf(" %d ", Thread_Info[j].thread_request[k]);
cout<<endl;
}
printf("\n\n");
//创建在模拟过程中几个必要的信号量
empty_semaphore=CreateSemaphore(NULL,n_Buffer_or_Critical,n_Buffer_or_Critical,
"semaphore_for_empty");
h_mutex = CreateMutex(NULL,FALSE,"mutex_for_update");
/*
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
// pointer to security attributes
LONG lInitialCount, // initial count
LONG lMaximumCount, // maximum count
LPCTSTR lpName // pointer to semaphore-object name
);
*/
/*
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
// pointer to security attributes
BOOL bInitialOwner, // flag for initial ownership FALSE表示初始时未被占用
LPCTSTR lpName // pointer to mutex-object name
);
*/
//下面这个循环用线程的ID号来为相应生产线程的产品读写时所
//使用的同步信号量命名;
for(j=0;j<(int)n_Thread;j++){
std::string lp ="semaphore_for_produce_";//定义了一个字符串
int temp =j;
while(temp){
char c = (char)(temp%10);
lp+=c;
temp/=10;
}
h_Semaphore[j+1]=CreateSemaphore(NULL,0,n_Thread,lp.c_str());//std
//这里循环创建了一个信号量数组,其中的每个信号的初值为0,最大值为实际线程的数目
}
//创建生产者和消费者线程;
for(i =0;i< (int) n_Thread;i++){
if(Thread_Info[i].entity =='P')
h_Thread[i]= CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Produce),
&(Thread_Info[i]),0,NULL);
else
h_Thread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)(Consume),
&(Thread_Info[i]),0,NULL);
/*
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to security attributes
DWORD dwStackSize, // initial thread stack size
LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function
LPVOID lpParameter, // argument for new thread
DWORD dwCreationFlags, // creation flags
LPDWORD lpThreadId // pointer to receive thread ID
);
*/
}
//主程序等待各个线程的动作结束;/*在这期间生产者和消费者进程交替执行*/
wait_for_all = WaitForMultipleObjects(n_Thread,h_Thread,TRUE,-1);
/*
DWORD WaitForMultipleObjects(
DWORD nCount, // number of handles in the handle array
CONST HANDLE *lpHandles, // pointer to the object-handle array
BOOL fWaitAll, // wait flag
DWORD dwMilliseconds // time-out interval in milliseconds
);
*/
printf(" \n \nALL Producer and consumer have finished their work. \n");
printf("Press any key to quit!\n");
_getch();
return 0;
}
//确认是否还有对同一产品的消费请求未执行;
bool IfInOtherRequest(int req)/*查找所有线程的请求队列,看是否有对req号产品的消费请求*/
{
for(int i=0;i<n_Thread;i++)
for(int j=0;j<Thread_Info[i].n_request;j++)
if(Thread_Info[i].thread_request[j] == req)
return TRUE;
return FALSE;
}
//找出当前可以进行产品生产的空缓冲区位置;
int FindProducePosition()
{
int EmptyPosition;
for (int i =0;i<n_Buffer_or_Critical;i++)
if(Buffer_Critical[i] == -1){
EmptyPosition = i;
//用下面这个特殊值表示本缓冲区正处于被写状态;
Buffer_Critical[i] = -2;
break;
}
return EmptyPosition;
}
//找出当前所需生产者生产的产品的位置;
/*每个生产者生产后产品都打着自己的线程序号,ProPos为提供的线程序号,根据它可查到产品在缓冲区的存放位置*/
int FindBufferPosition(int ProPos)
{
int TempPos;
for (int i =0 ;i<n_Buffer_or_Critical;i++)
if(Buffer_Critical[i]==ProPos){
TempPos = i;
break;
}
return TempPos;
}
//生产者进程
void Produce(void *p)
{
/*
生产者在生产时,
1首先要申请empty_semaphore资源信号量(该信号量值为空缓冲区的数目),判断是否有空缓冲区可用
2然后要申请h_mutex互斥信号量(该信号量专用于生产者进程在查询缓冲区状态的互斥,不允许多个生产者
同时查询)
3然后可以在缓冲区队列中找空缓冲区的位置
4找到后要释放h_mutex
5进行生产
6给该产品的h_Semaphore[m_serial]信号量值加n_Thread,以使消费者可进行消费
*/
//局部变量声明;
DWORD wait_for_semaphore,wait_for_mutex,m_delay;
int m_serial;
//获得本线程的信息;
m_serial = ((ThreadInfo*)(p))->serial;
m_delay = (DWORD)(((ThreadInfo*)(p))->delay *INTE_PER_SEC);
Sleep(m_delay); //延时
//开始请求生产
printf("Producer %2d sends the produce require.\n",m_serial);
//确认有空缓冲区可供生产,同时将空位置数empty减1;用于生产者和消费者的同步;
wait_for_semaphore = WaitForSingleObject(empty_semaphore,-1);//empty_semaphore为资源信号量
/*这里首先看一看有没有空闲的缓冲区可供存放生产的产品,如果没有,等待(消费者消费掉)直到有。*/
//互斥访问下一个可用于生产的空临界区,实现写写互斥;
/*去拿h_mutex互斥信号量,该信号量是为整个缓冲区而设的,用以实现生产者在进行缓冲区状态查询时
的互斥。查询时,如果找到空缓冲区,要修改其状态标记,要求各生产者必须互斥地进行*/
wait_for_mutex = WaitForSingleObject(h_mutex,-1);//h_mutex为互斥信号量,-1代表永不超时
int ProducePos = FindProducePosition();
ReleaseMutex(h_mutex);
/*
DWORD WaitForSingleObject(
HANDLE hHandle, // handle to object to wait for
DWORD dwMilliseconds // time-out interval in milliseconds
);
*/
//生产者在获得自己的空位置并做上标记后,以下的写操作在生产者之间可以并发;
//核心生产步骤中,程序将生产者的ID作为产品编号放入,方便消费者识别;
printf("Producer %2d begin to produce at position %2d.\n",m_serial,ProducePos);
Buffer_Critical[ProducePos] = m_serial; //生产者的编号作为产品的编号存入缓冲区
printf("Producer %2d finish producing :\n ",m_serial);
printf(" position[ %2d ]:%3d \n" ,ProducePos,Buffer_Critical[ProducePos]);//在缓冲区的哪个位置放置了哪种产品
//使生产者写的缓冲区可以被多个消费者使用,实现读写同步;
ReleaseSemaphore(h_Semaphore[m_serial],n_Thread,NULL);//进程m_serial生产的产品对应的信号量值加m_Thread,允许多个消费者依次使用
//如果h_semaphore[i]的值始终为0的话,消费者进程只能等待
/*
BOOL ReleaseSemaphore(
HANDLE hSemaphore, // handle to the semaphore object
LONG lReleaseCount, // amount to add to current count
LPLONG lpPreviousCount // address of previous count
);
*/
}
//消费者进程
void Consume(void * p)
{
/*消费者在消费某一个产品时
1.申请该产品的h_Semaphore[m_serial]资源信号量,如果值为0,说明当前该种产品尚未有产品,等待
2.查找到该产品在缓冲区队列中的存放位置具体在哪一个缓冲区
3.进入该缓冲区的临界区PC_Critical[BufferPos](只允许一个进程进入)
4.消费该种产品
5.判断还有没有其它进程存在对该种产品的消费请求,如果没有了,则可释放该产品所占的缓冲区
6.退出缓冲区
*/
//局部变量声明;
DWORD wait_for_semaphore,m_delay;
int m_serial,m_requestNum; //消费者的序列号和请求的数目;
int m_thread_request[MAX_THREAD_NUM];//本消费线程的请求队列;(它想消费哪几个生产者进程生产的产品,数组里面相应生产者编号)
//提取本线程的信息到本地;
m_serial = ((ThreadInfo*)(p))->serial;
m_delay = (DWORD)(((ThreadInfo*)(p))->delay *INTE_PER_SEC);
m_requestNum = ((ThreadInfo *)(p))->n_request;
for (int i = 0;i<m_requestNum;i++)
m_thread_request[i] = ((ThreadInfo*)(p))->thread_request[i];//请求队列
Sleep(m_delay);
//循环进行所需产品的消费
for(i =0;i<m_requestNum;i++){
//请求消费下一个产品
printf("Consumer %2d request to consume %2d product\n",m_serial,m_thread_request[i]);
//如果对应生产者没有生产,则等待;如果生产了,允许的消费者数目-1;实现了读写同步;
wait_for_semaphore=WaitForSingleObject(h_Semaphore[m_thread_request[i]],-1); //-1永不超时
//查询所需产品放到缓冲区的号
int BufferPos=FindBufferPosition(m_thread_request[i]);
//开始进行具体缓冲区的消费处理,读和读在该缓冲区上仍然是互斥的;
//进入临界区后执行消费动作;并在完成此次请求后,通知另外的消费者本处请求已
//经满足;同时如果对应的产品使用完毕,就做相应处理;并给出相应动作的界面提
//示;该相应处理指将相应缓冲区清空,并增加代表空缓冲区的信号量;
EnterCriticalSection(&PC_Critical[BufferPos]);//进入该缓冲区的临界区
printf("Consumer%2d begin to consume %2d product \n",m_serial,m_thread_request[i]);
((ThreadInfo*)(p))->thread_request[i] =-1;//将该消费者进程请求队列对该产品消费请求置为-1,代表当前已不再请求消费该产品
if(!IfInOtherRequest(m_thread_request[i])){
//如果没有其它进程的请求,则该产品可被释放了
Buffer_Critical[BufferPos] = -1;//标记缓冲区为空;
printf("Consumer%2d finish consuming %2d:\n ",m_serial,m_thread_request[i]);
printf(" position[ %2d ]:%3d \n" ,BufferPos,Buffer_Critical[BufferPos]);
ReleaseSemaphore(empty_semaphore,1,NULL);//给缓冲区信号量加1,用于与生产者同步
}
else{
printf("Consumer %2d finish consuming product %2d\n ",m_serial,m_thread_request[i]);
}
//离开临界区
LeaveCriticalSection(&PC_Critical[BufferPos]);
}
}
//循环向a函数每次发送200个字节长度(这个是固定的)的buffer,
//a函数中需要将循环传进来的buffer,组成240字节(也是固定的)的新buffer进行处理,
//在处理的时候每次从新buffer中取两个字节打印
#ifdef WIN32
#pragma warning(disable:4996)
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#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 WIN32
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,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 WIN32
pthread_exit(NULL);
#endif
}
int PutToRBuf(int cn,CRITICAL_SECTION *cs,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 WIN32
pthread_exit(NULL);
#endif
}
int main() {
#ifdef WIN32
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 WIN32
_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 WIN32
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;
}