[ Home | Syllabus | Course Notes | Assignments | Search]
VARIABLE:
FUNCTIONS:
#include <windows.h> //. . . volatile INT count; CRITICAL_SECTION critSec; void CountThread(INT iterations) { //. . . for (i=0; i<iterations; i++) { EnterCriticalSection(&critSec); x=count; x++; count=x; LeaveCriticalSection(&critSec); } } void main(void) { // . . . InitializeCriticalSection(&critSec); for (i=0; i<numThreads; i++) { // create a thread and pass it the pointer // to its "params" struct handles[i]=CreateThread(0, 0, (LPTHREAD_START_ROUTINE) CountThread, (VOID *) 25000, 0, &threadID); } WaitForMultipleObjects(numThreads, handles, TRUE, INFINITE); DeleteCriticalSection(&critSec); }
Can be used across processes by naming it.
VARIABLE:
FUNCTIONS:
void CountThread(INT iterations) { INT i; INT x; HANDLE mutex; mutex = CreateMutex(0, FALSE, "counterMutex"); // only first thread actually creates it for (i=0; i<iterations; i++) { WaitForSingleObject(mutex, INFINITE); x=count; x++; count=x; ReleaseMutex(mutex); } CloseHandle(mutex); }
In automatic mode, like a MUTEX, in manual can be used to release a number of waiting processes/threads.
Used to wait for many things (p. 257)
Also allows a time out value.
Multiple Objects can be waited for
Bounded buffer is a classic problem in operating system synchronization. A producer and consumer exchange information using a fixed number of buffers. They can run at different rates and should never get into an inconsistent state
There are two solutions available
We will examine the later here. Points to be demonstrated:
#include <windows.h> #include <iostream.h> #include <stdlib.h> const int NBUFFERS = 3; // allow 3 buffers for bounded buffer typedef struct{ int bufID; int bufValue; } Buffer; Buffer commonBuffers[NBUFFERS]; void producer(DWORD myId); // producer thread function void consumer(DWORD myId); // consumer thread function void main(void) { HANDLE empty, full; // "pointer" to the semaphores HANDLE testHandles[2]; // "pointers" to the threads DWORD threadId; // receives threadID assigned (not used mostly) int id = 1; // one parameter to thread is an integer ID // Create the semaphores empty = CreateSemaphore(0,NBUFFERS,NBUFFERS, "NTcourse.semaphore.empty"); // Create a counting semaphore with initial value NBUFFERS and max value NBUFFERS // This semaphore controls the empty buffer (initially all buffers are empty) full = CreateSemaphore(0,0,NBUFFERS, "NTcourse.semaphore.full"); // Create a counting semaphore with initial value NBUFFERS and max value NBUFFERS // This semaphore controls the full buffers (initially no full buffers) // Start producer (with argument, id = 1) testHandles[0] = CreateThread(0,0, (LPTHREAD_START_ROUTINE) producer, (void*)id, 0, &threadId); id++; // Start consumer (with argument ,id = 2) testHandles[1] = CreateThread(0,0, (LPTHREAD_START_ROUTINE) consumer, (void*)id, 0, &threadId); WaitForMultipleObjects(2, testHandles, TRUE, INFINITE); // wait until both threads are done
CloseHandle(empty); CloseHandle(full); } //*************************************************************** // illustrates two processes cooperating in a producer/consumer relationship // The producer/consumer relationship is managed by two semaphores // EMPTY and FULL, When EMPTY the producer can produce into empty buffer, // when FULL the consumer can consume buffer. // the buffer is passed through shared memory using the file mapping mechanism // This code demonstrates the consumer side // AUTHOR: Chris Wild // DATE: September 10, 1997 //*************************************************************** void producer(DWORD myId) { HANDLE empty, full; empty = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE, "NTcourse.semaphore.empty"); // opens semaphore created in the main thread if(empty == NULL) { cout << "Error opening empty: " << GetLastError() << endl; ExitProcess(1); } full = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, "NTcourse.semaphore.full"); if(full == NULL) { cout << "Error opening full: " << GetLastError() << endl; ExitProcess(1); } // enter a loops producing some things int bufN = 0; for(int i = 0; i < 10; i++) { // First wait for previous resource to be empty (consumed) cout << "PRODUCER: waiting for empty buffer\n"; WaitForSingleObject(empty, INFINITE); // Have empty resource - use it to produce // put code here to produce something commonBuffers[bufN].bufID = bufN; // buffer number commonBuffers[bufN].bufValue = 1000*i; // buffer value bufN = (bufN+1) % NBUFFERS; ReleaseSemaphore(full,1,0); // signals production } CloseHandle(empty); CloseHandle(full); } void consumer(DWORD myId) { HANDLE empty, full; // control bounded buffer access empty = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE, "NTcourse.semaphore.empty"); if(empty == NULL) { cout << "Error opening empty: " << GetLastError() << endl; ExitProcess(1); } full = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, "NTcourse.semaphore.full"); if(full == NULL) { cout << "Error opening full: " << GetLastError() << endl; ExitProcess(1); } // enter a loops consuming some things int bufN = 0; DWORD returnValue; while(true) { // First wait for buffer to be produced returnValue = WaitForSingleObject(full, 10000); // wait 10 seconds if( returnValue == WAIT_TIMEOUT) { cout << "Waited too long for producer - assumed died\n"; break; } // Have full buffer - consume it bufN = (bufN+1) % NBUFFERS; ReleaseSemaphore(empty,1,0); // signals empty } CloseHandle(empty); CloseHandle(full); }