Advertisment

Designing Threaded Applications

author-image
PCQ Bureau
New Update

Last month, we created a worker thread class and used it to design a worker thread pool. Continuing from there, we’ll talk in some detail about thread functions here.

Advertisment

Thread functions, used by the threads from the pool, should have the following prototype and implementation.

DWORD ThreadFunction(LPVOID p)



{


// copy the thread-specific data


CThreadData *pData=(CThreadData *)p;


DWORD tid=pData->index;


while(1)


{


// wait until signaled to work...


WaitForSingleObject(pData->hBusy,INFINITE);


// get data


DWORD num=pData->data;


// increment the used count


pData->Used++;


// do your work here...


// ask to terminate thee ?


if (WaitForSingleObject(pData->hTerminate,0)==WAIT_OBJECT_0)


break;


// reset thee


ResetEvent(pData->hBusy);


SetEvent(pData->hFree);


Sleep(0);


}


printf(“Thread died %ld\n”,pData->index);


return 0;


}






















The thread function shall take a void pointer to the data for its use and return a DWORD (unsigned long).

Advertisment

Upon instantiation, the CThreadPool constructor starts off by creating an array of CThreadData objects, one for each worker thread in the pool. Creation of this array results in the calling of the constructor of the CThreadData class, which initializes various thread parameters and events as described earlier, on a per-thread basis. Next, the worker threads are created, with their handles and IDs being copied to each thread’s respective CThreadData object. The creation of CThreadData object doesn’t set the busy event. Also, the creation of the worker thread in the constructor of CThreadPool immediately sends control to the thread function (we haven’t used the CREATE_SUSPENDED flag if you notice), making it execute. Thus, to make the worker thread wait until it has a client request to be serviced, the worker thread is made to wait indefinitely using the WaitForSingleObject API within the while loop, in the ThreadFunction, until the busy event is signaled. The thread count is saved next, followed by initializing the index of the next available free thread. This index is used by the thread selection algorithm (in WorkThePool) to select a free thread to service an incoming request.

Once the CThreadPool object has been instantiated, the client calls the WorkThePool method to send a client request to be serviced by a worker thread. WorkThePool takes a void pointer to the data, which is to be passed to the thread. It starts from the current value of the index used to indicate the next free worker thread, and moves in a circular fashion to find the next free worker thread. Once a free worker thread is found, it enters a critical section and resets the free event of the thread, making the thread non-free, but not busy either. Doing this from within a critical section is very important, because the critical section ensures that only one thread at a time can reset its free event, making the thread non-free.

Once this is done, the data for the thread is copied to the CThreadData object and the thread’s busy event is signaled. Note that, till now, the thread had been waiting indefinitely, in the while-loop in the thread function, on the WaitFor SingleObject for the busy event. The moment the busy event is signaled by WorkThePool, the worker thread comes to life, increments its usage count, does whatever work it has to, and then checks if it was signaled for termination. If so, it then breaks out of the loop and terminates normally. If termination has not been signaled, the busy and free events are reset indicating that the thread is now free to accept a new request.

Kumar Gaurav Khanna runs www.wintools.f2s.com

Advertisment