///////////////////////////////////////////////////////////////////// // race.cpp - demonstrate race condition // // // // Two threads attempt to run to completion, but second one // // often blocks, waiting for output to finish, so only first // // finishes. // // Jim Fawcett, CSE691 - Software Modeling & Analysis, Fall 2000 // ///////////////////////////////////////////////////////////////////// #include #include #include using namespace std; // // A race condition occurs when the fundamental behavior of // your processing depends on the relative speed with which // individual threads do their work. // // In this program one worker thread does all computation (simply // incrementing an integer), while another worker thread does the // same, but also sends the result to standard output, a slow // process. // // The main thread (which created the two workers) only waits for // the first to finish, then terminates, which also kills the // remaining thread, which still has more work to do. Thus // thread 2 always loses the race, and never finishes. In fact // it generates an incorrect result, claiming that it did zero // iterations, because it never got to update its iteration count // at the end. // //----< thread processing function >--------------------------- unsigned int __stdcall threadProc1(void *pData) { int i, count=0; for(i=0; i<1000; i++) ++count; *((int*)pData) = count; return 0; } //----< thread processing function >--------------------------- unsigned int __stdcall threadProc2(void *pData) { int i, count=0; for(i=0; i<1000; i++) { ++count; // this output statement slows down the loop // causing a race condition cout << "\n thread #2's count = " << count; cout.flush(); } // due to race condition in this program // this statement will never be executed *((int*)pData) = count; return 0; } // //----< entry point for process >------------------------------ void main() { cout << "\n Demonstrate the Operation of Mult-threaded Code " << "\n With Race Condition" << "\n=======================================================\n\n"; // define an array of handles to threads, needed to wait // for their completion const int NumHandles = 2; HANDLE thHand[NumHandles]; int count1 = 0, count2 = 0; // define argument values for thread #1 _beginthreadex string shared; void* Security = NULL; // no security attributes unsigned int StackSize = 0; // stack size picked by OS unsigned int(__stdcall threadProc1)(void*); // name of thread function unsigned int InitFlag = 0; // start running unsigned int ThreadID[2]; // receives thread id void *Arg1 = &count1; // pointer to argument // start first thread thHand[0] = (HANDLE)_beginthreadex( Security, StackSize, threadProc1, Arg1, InitFlag, &ThreadID[0] ); // // define argument values for thread #2 _beginthreadex void *Arg2 = &count2; // start second thread thHand[1] = (HANDLE)_beginthreadex( Security, StackSize, threadProc2, Arg2, InitFlag, &ThreadID[1] ); // tell process to wait for both threads to complete // before terminating BOOL fwait = FALSE; // wait for first unsignaled handle DWORD timeOut = INFINITE; // time out in milliseconds WaitForMultipleObjects(NumHandles, thHand, fwait, timeOut); CloseHandle(thHand[0]); // return resources CloseHandle(thHand[1]); // return resources Sleep(5); cout << "\n count1 = " << count1 << "\n count2 = " << count2 << "\n\n"; }