现在本人知道C/C++多线程同步有互斥锁、信号量、Event等几种方式,但是以前本人不知道线程同步概念的时候,用全局变量控制了两个线程的执行顺序,就是线程1->线程2->线程1->线程2……
请大家看下这种方式是不是有漏洞,举个简单例子。
请大家看下这种方式是不是有漏洞,举个简单例子。
//MultiThread #include <iostream> #include <cstdlib> #include <windows.h> using namespace std; #define MUTELOCK DWORD WINAPI Fun1Proc(LPVOID lpParameter); DWORD WINAPI Fun2Proc(LPVOID lpParameter); int tickets = 200; HANDLE hMutex; int fun1over = 0; int fun2over = 1; int main() { hMutex = CreateMutex(NULL, FALSE, NULL); HANDLE hThread_1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL); HANDLE hThread_2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL); CloseHandle(hThread_1); cout << "close handle1" << endl; CloseHandle(hThread_2); cout << "close handle2" << endl; system("pause"); return 0; } DWORD WINAPI Fun1Proc(LPVOID lpParameter) { while (true) { #ifdef MUTELOCK WaitForSingleObject(hMutex, INFINITE); if (tickets > 0) { cout << "Thread 1 sell ticket : "<<tickets--<<endl; } else break; ReleaseMutex(hMutex); #else if (fun2over) { fun2over = 0; if (tickets > 0) { cout << "Thread 1 sell ticket : "<<tickets--<<endl; } else break; fun1over = 1; } #endif } return 0; } DWORD WINAPI Fun2Proc(LPVOID lpParameter) { while (true) { #ifdef MUTELOCK WaitForSingleObject(hMutex, INFINITE); if (tickets > 0) { cout << "Thread 2 sell ticket : "<<tickets--<<endl; } else break; ReleaseMutex(hMutex); #else if (fun1over) { fun1over = 0; if (tickets > 0) { cout << "Thread 2 sell ticket : "<<tickets--<<endl; } else break; fun2over = 1; } #endif } return 0; }
打开和关闭MUTELOCK,运行结果是没有区别的。
解决方案
8
这一点在《Windows核心编程》第五版有讲,全局变量肯定不合理
4
各个线程读到的 全局变量的值又不是一致的, 你的代码当然是错的…
这代码看起来正常仅仅是原因是 cout IO时内部是加了锁的, 这刷新了CPU的高速缓存, 而其他代码运行时间相对于IO , 几乎可以忽略.. 你把中间处理的代码换成一个不用加锁的处理代码 就可以发现问题了…
这代码看起来正常仅仅是原因是 cout IO时内部是加了锁的, 这刷新了CPU的高速缓存, 而其他代码运行时间相对于IO , 几乎可以忽略.. 你把中间处理的代码换成一个不用加锁的处理代码 就可以发现问题了…
4
仅供参考:
#include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef WIN32 #include <windows.h> #include <io.h> #include <process.h> #define MYVOID void #else #include <unistd.h> #include <sys/time.h> #include <pthread.h> #define CRITICAL_SECTION pthread_mutex_t #define _vsnprintf vsnprintf #define MYVOID void * #endif //Log{ #define MAXLOGSIZE 20000000 #define ARRSIZE(x) (sizeof(x)/sizeof(x[0])) #include <time.h> #include <sys/timeb.h> #include <stdarg.h> char logfilename1[]="MyLog1.log"; char logfilename2[]="MyLog2.log"; char logstr[16000]; 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; if (-1==_vsnprintf(logstr,ARRSIZE(logstr),pszFmt,argp)) logstr[ARRSIZE(logstr)-1]=0; 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); } flog=fopen(logfilename1,"a"); if (NULL==flog) return; } 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} static int volatile No_Loop=0; static int volatile gLock = 0; static int volatile gCounter = 0; MYVOID testThread(void *pcn) { int n,i; n=(int)pcn; while (1) { if (No_Loop==2) { for (i = 0; i < 1000000; ) { //以下代码无效 // if ( 0 == gLock ) { // gLock = n; // if( gLock == n ) { // ++i; // ++gCounter; // //if (i%100000==0) Log("%d %d\n",n,gCounter); // gLock = 0; // //if (i%100000==0) sleep_ms(10*(rand()%50)); // continue; // } else sleep_ms(100); // } else sleep_ms(100); //应改为以下代码 InterlockedCompareExchange((void **)&gLock, (void *)n, 0); if( gLock == n ) { ++i; ++gCounter; if (i%100000==0) Log("%d %d\n",n,gCounter); InterlockedExchange((long *)&gLock, 0); if (i%100000==0) sleep_ms(10*(rand()%50)); continue; } else sleep_ms(100); } sleep_ms(1000); No_Loop=1; } } } int main(int argc,char * argv[]) { int i; srand(time(NULL)); #ifdef WIN32 InitializeCriticalSection(&cs_log); #else pthread_mutex_init(&cs_log,NULL); pthread_t threads[4]; // void *thread_result; int threadsN; int rc; #endif Log("=========BEGIN==================\n"); #ifdef WIN32 _beginthread((void(__cdecl *)(void *))testThread,0,(void *)1); _beginthread((void(__cdecl *)(void *))testThread,0,(void *)2); _beginthread((void(__cdecl *)(void *))testThread,0,(void *)3); _beginthread((void(__cdecl *)(void *))testThread,0,(void *)4); #else threadsN=0; rc=pthread_create(&(threads[threadsN++]),NULL,testThread,(void *)1);if (rc) Log("%d=pthread_create %d error!\n",rc,threadsN-1); rc=pthread_create(&(threads[threadsN++]),NULL,testThread,(void *)2);if (rc) Log("%d=pthread_create %d error!\n",rc,threadsN-1); rc=pthread_create(&(threads[threadsN++]),NULL,testThread,(void *)3);if (rc) Log("%d=pthread_create %d error!\n",rc,threadsN-1); rc=pthread_create(&(threads[threadsN++]),NULL,testThread,(void *)4);if (rc) Log("%d=pthread_create %d error!\n",rc,threadsN-1); #endif sleep_ms(1000); No_Loop=2; i=0; while (1) { sleep_ms(1000); if (No_Loop==1) break;// } sleep_ms(1000); Log("Result: %d\n",gCounter); Log("=========END====================\n"); #ifdef WIN32 DeleteCriticalSection(&cs_log); #else pthread_mutex_destroy(&cs_log); #endif return 0; }
4
那个是在 单核 单CPU 时代的问题, 到了 多核/多CPU的时候, 即使 ++, — , += -= 啥的都用单条指令实现( incl, decl, xadd …) 一样是错误的, 这条指令只是修改了当前核心 L1 Cache 中的内容, 在高速缓存刷新周期内, 其他 CPU , 其他核心根本就不能发现变量的值已经发生了改变 …