进程与线程(四)线程中访问资源的同步

来源:互联网 发布:淘宝详情页750改950 编辑:程序博客网 时间:2024/05/12 09:42
一种方法就是使用关键段(Critical Section)进行全局变量的处理,关键段不宜过于复杂

初始化的时候,建议使用InitializeCriticalSectionAndSpinCount而不单是InitializeCriticalSection,如果不使用旋转锁,当我们这个线程要访问的数据正在被其他线程处理时,将会被切换到等待状态,线程将从用户模式进入内核模式,这个过程中将浪费很多时间,也许在线程进入内核之前,资源已经被其他线程释放,所以我们加一个旋转锁处理。当然,在单核cpu下这种处理是毫无意义的,因为我们的线程陷入循环,某个正在占用资源的线程将没有机会处理和释放这个资源,这个不必我们多虑,在单核cpu中,InitializeCriticalSectionAndSpinCount的第二个参数dwSpinCount会被忽略,认为是0.

下面是个例子,当然如果只是这么一个简单的计算,我们完全没必要多线程,直接用等差数列的方程就可以解决,这么做反而会降低工作效率,这里只是一个例子。
  1 DWORD g_num;   2 CRITICAL_SECTION cs;   3   4 // 唯一的应用程序对象   5   6 CWinApp theApp;   7   8 using namespace std;   9  10 int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])  11 {  12     _tsetlocale(LC_ALL,_T("chs") );  13     ::system("title ReturnsMe的win32api测试程序");  14  15     g_num = 0;  16  17     //初始化关键段和旋转锁  18     InitializeCriticalSectionAndSpinCount(&cs,4000);  19      20     //开启10线程,每个线程执行g_num累加,  21     HANDLE h[10];  22     for (int i=0;i<10;i++)  23     {  24         h[i] = chBEGINTHREADEX(NULL,NULL,FuncThread,NULL,0,NULL);  25      }  26  27     WaitForMultipleObjects(10,h,TRUE,INFINITE);  28  29     //由于我们使用了同步处理,不出意外的话,应该打印出50500  30     wprintf(_T("num is %d  \n"),g_num);  31  32     //打印完毕,我们也就不需要这个关键段了,应该删掉  33     DeleteCriticalSection(&cs);  34  35     system("pause");  36  37     return 0;  38 }  39 DWORD WINAPI FuncThread(PVOID pvParam)  40 {  41     //进入关键段  42     EnterCriticalSection(&cs);  43  44     for (int i=0;i<=100;i++)  45     {  46         g_num = g_num + i;  47     }  48  49     //离开  50     LeaveCriticalSection(&cs);  51  52     return 0;  53 }
另一种办法是使用SRWLock 在某些情况下,性能要比CS好一些,但功能却也有一些限制,特别注意的是,SRWLock允许多线程同时访问一个资源,但由于windows是抢占式操作系统,哪个线程在什么时候谁先谁后的访问一个资源是不确定的,所以我们也看不出什么时候同时访问了资源。

所以下面这段代码中,不会有一个确定的打印结果


  1 DWORD g_num;   2 SRWLOCK srw_lock;   3   4   5 // 唯一的应用程序对象   6   7 CWinApp theApp;   8   9 using namespace std;  10  11 int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])  12 {  13     _tsetlocale(LC_ALL,_T("chs") );  14     ::system("title ReturnsMe的win32api测试程序");   15  16     g_num = 0;  17       18     //初始化一个锁,与CS不同,用完之后不用删除,因为系统会帮助我们做这些事情。  19      20     InitializeSRWLock(&srw_lock);  21  22     HANDLE hShared;  23       24     //开启10线程,每个线程执行g_num累加,在开启一个读取即时的g_num数值  25     HANDLE h[10],hh[10];  26     for (int i=0,j=0;i<10;i++,j++)  27     {  28         h[i] = chBEGINTHREADEX(NULL,NULL,ExclusiveThread,NULL,0,NULL);  29         hh[j] = chBEGINTHREADEX(NULL,NULL,SharedThread,NULL,0,NULL);  30     }   31       32  33     WaitForMultipleObjects(10,h,TRUE,INFINITE);  34     WaitForMultipleObjects(10,hh,TRUE,INFINITE);  35  36     wprintf(_T("finally,g_num is %d \n"),g_num);  37  38     system("pause");  39  40     return 0;  41 }  42 DWORD WINAPI ExclusiveThread(PVOID pvParam)   43 {      44     //写入资源,所以采用独占模式  45     AcquireSRWLockExclusive(&srw_lock);  46  47     for (int i=0;i<=100;i++)  48     {  49         g_num = g_num + i;  50     }  51  52     ReleaseSRWLockExclusive(&srw_lock);  53  54     return 0;  55 }  56 DWORD WINAPI SharedThread(PVOID pvParam)   57 {  58     //读取资源,共享模式  59     AcquireSRWLockShared(&srw_lock);  60  61     wprintf(_T("g_num is %d \n"),g_num);  62  63     ReleaseSRWLockShared(&srw_lock);  64  65     return 0;  66 }

可以使用条件变量,使一个线程在资源没有更新的情况下进入睡眠,等待写入资源的线程来唤醒。


  1 DWORD g_num;   2 SRWLOCK srw_lock;   3 CONDITION_VARIABLE g_cv;   4   5 // 唯一的应用程序对象   6   7 CWinApp theApp;   8   9 using namespace std;  10  11 int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])  12 {  13     _tsetlocale(LC_ALL,_T("chs") );  14     ::system("title ReturnsMe的win32api测试程序");   15  16     g_num = 0;  17       18     //初始化一个锁  19     InitializeSRWLock(&srw_lock);  20       21  22     HANDLE h[2];  23  24  25     h[1]= chBEGINTHREADEX(NULL,NULL,SharedThread,NULL,0,NULL);  26     h[0] = chBEGINTHREADEX(NULL,NULL,ExclusiveThread,NULL,0,NULL);  27  28  29     WaitForMultipleObjects(2,h,TRUE,INFINITE);  30  31     wprintf(_T("finally,g_num is %d \n"),g_num);  32  33     system("pause");  34  35     return 0;  36 }  37 DWORD WINAPI ExclusiveThread(PVOID pvParam)   38 {      39     //写入资源,所以采用独占模式  40     AcquireSRWLockExclusive(&srw_lock);  41  42     for (int i=0;i<=100;i++)  43     {  44         g_num = g_num+ i;  45     }  46  47     Sleep(2000);  48  49     //线程进行太快效果不明显,这里睡眠2s后唤醒等待线程  50  51     WakeConditionVariable(&g_cv);  52  53     ReleaseSRWLockExclusive(&srw_lock);  54  55     return 0;  56 }  57 DWORD WINAPI SharedThread(PVOID pvParam)   58 {  59     //读取资源,采用共享模式  60       61     AcquireSRWLockShared(&srw_lock);  62  63     wprintf(_T("g_num is %d \n"),g_num);  64  65     wprintf(_T("waiting for the variable to be changed  \n"));  66  67     //进入睡眠,第三个变量也分共享模式和独占模式,独占传入0,共享的情况如下所示。  68  69     SleepConditionVariableSRW(&g_cv,&srw_lock,INFINITE,CONDITION_VARIABLE_LOCKMODE_SHARED);  70  71     wprintf(_T("g_num has been changed, it's %d \n"),g_num);  72  73     ReleaseSRWLockShared(&srw_lock);  74  75     return 0;  76 }