下面起了两个线程,每个对一个全局变量加500次,不假思索进行回答,会认为最后这个全局变量的值会是1000,然而事实并不是这样:
#include<iostream> #include <process.h> #include <windows.h> using namespace std; typedef unsigned int (_stdcall *PThreadFunc)(void*); int g_nCount = 0; unsigned int _stdcall ThreadTest1(void*) { for (int i = 0; i < 500; i++) { g_nCount++; } return 0; } unsigned int _stdcall ThreadTest2(void*) { for (int i = 0; i < 500; i++) { g_nCount++; } return 0; } void main() { g_nCount = 0; HANDLE h1 = (HANDLE)_beginthreadex(NULL, 0, ThreadTest1, NULL, 0, NULL); HANDLE h2 = (HANDLE)_beginthreadex(NULL, 0, ThreadTest2, NULL, 0, NULL); HANDLE hs[2] = {h1, h2}; WaitForMultipleObjects(2, hs, TRUE, INFINITE); CloseHandle(h1); CloseHandle(h2);
printf("Global count:%d ", g_nCount); getchar();
}
然而运行多次、每次结果都不同,而且,几乎不会等于1000:
造成这种现象的原因很简单,就是g_nCount在进行自增的时候没有实现原子操作,g_nCount的本质其实是:
- Interlocked函数
为了保证自增的原子性,改为使用Interlocked函数:
#include<iostream> #include <process.h> #include <windows.h> using namespace std; typedef unsigned int (_stdcall *PThreadFunc)(void*); int g_nCount = 0; unsigned int _stdcall ThreadTest1(void*) { for (int i = 0; i < 500; i++) { //Sleep(12); //g_nCount ++; InterlockedIncrement((volatile unsigned long long*)&g_nCount); } return 0; } unsigned int _stdcall ThreadTest2(void*) { for (int i = 0; i < 500; i++) { //Sleep(10); //g_nCount ++; InterlockedIncrement((volatile unsigned long long*)&g_nCount); } return 0; } void main() { g_nCount = 0; HANDLE h1 = (HANDLE)_beginthreadex(NULL, 0, ThreadTest1, NULL, 0, NULL); HANDLE h2 = (HANDLE)_beginthreadex(NULL, 0, ThreadTest2, NULL, 0, NULL); HANDLE hs[2] = { h1, h2 }; WaitForMultipleObjects(2, hs, TRUE, INFINITE); CloseHandle(h1); CloseHandle(h2); printf("Global count:%d ", g_nCount); getchar(); }
这样就保证了自增的原子性。
- 条件变量的使用
#include <iostream> #include <windows.h> #include <vector> #include <process.h> #include "Queue.h" using namespace std; CQueue g_Queue; SRWLOCK g_srwLock; CONDITION_VARIABLE g_cvProduce; CONDITION_VARIABLE g_cvConsume; int g_nCount = 0; int g_nWriterCount = 0; int g_nReaderCount = 0; unsigned int _stdcall WriterThread(void* pParam) { g_nWriterCount++; //printf("Enter writerthread-%d ", g_nWriterCount); while (TRUE) { Sleep(1000); AcquireSRWLockExclusive(&g_srwLock); if (g_Queue.IsFull()) { printf("Queue is full.. "); SleepConditionVariableSRW(&g_cvProduce, &g_srwLock, INFINITE, 0); } /*else { }*/ g_Queue.AddElement(g_nCount); printf("Produce element:%d ", g_nCount); g_nCount++; ReleaseSRWLockExclusive(&g_srwLock); WakeConditionVariable(&g_cvConsume); } return 0; } unsigned int _stdcall ReaderThread(void* pParam) { g_nReaderCount++; //printf("Enter readerthread-%d ", g_nReaderCount); while (TRUE) { Sleep(2000); //这里使用的例子和书中的例子有所不同,书中的例子中的ReaderThread仅仅是读取队列中的内容,而这里
//会去修改队列的内容,所以不能使用AcquireSRWLockShared. AcquireSRWLockExclusive(&g_srwLock); if (g_Queue.IsEmpty()) { printf("Queue is empty.. "); SleepConditionVariableSRW(&g_cvConsume, &g_srwLock, INFINITE, 0); } /*else { }*/ printf("Consume element:%d ", g_Queue.DelElement()); ReleaseSRWLockExclusive(&g_srwLock); WakeAllConditionVariable(&g_cvProduce);//don't use wakeconditionvariable(). } return 0; } void main() { InitializeSRWLock(&g_srwLock); HANDLE hWriter1 = (HANDLE)_beginthreadex(NULL, 0, WriterThread, NULL, 0, NULL); HANDLE hWriter2 = (HANDLE)_beginthreadex(NULL, 0, WriterThread, NULL, 0, NULL); HANDLE hReader1 = (HANDLE)_beginthreadex(NULL, 0, ReaderThread, NULL, 0, NULL); HANDLE hReader2 = (HANDLE)_beginthreadex(NULL, 0, ReaderThread, NULL, 0, NULL); HANDLE hReader3 = (HANDLE)_beginthreadex(NULL, 0, ReaderThread, NULL, 0, NULL); HANDLE hArray[5] = { hWriter1, hWriter2, hReader1, hReader2, hReader3 }; WaitForMultipleObjects(5, hArray, TRUE, INFINITE); CloseHandle(hWriter1); CloseHandle(hWriter2); CloseHandle(hReader1); CloseHandle(hReader2); CloseHandle(hReader3); getchar(); }