Even More About Threads with win32 API

A Windows process doesn’t technically run; threads run, the process is simply a manager. Threads are the actual entities scheduled to execute code on calls. A thread by default runs under its parent process’s security context.

In our first program, we will create a thread to execute the classic “Hello World!” program. We will use the CreateThread() function, which takes six parameters. The first two parameters we will pass 0 to, the third parameter is the memory location of the function we wish to run, which we deliver in the form of the function’s name. The fourth parameter is the argument we wish to send to the function. The fifth argument will we specify 0 for, as we do not wish to pass in any special flags. The final argument is a pointer to a DWORD value to store the thread ID of the new thread.

 #include "stdafx.h"
#include <Windows.h>

//entry function for child thread
DWORD WINAPI ChildThreadFunc(LPVOID lpThreadParam) {
 //parameter is an integer value indicating
 //the number of seconds the thread should run for
 DWORD dwNumSeconds = (DWORD)lpThreadParam;
 DWORD dwThreadID = GetCurrentThreadId();
 while (dwNumSeconds-- > 0) {
 printf("Hello from thread ID %d\n", dwThreadID);
 Sleep(1000);
 }
 //lets return 0 
 return 0;
}

int main()
{
 HANDLE hChildThread;
 DWORD dwChildID, dwParentID, dwExitCode;

 //determine primary thread's ID number
 dwParentID = GetCurrentThreadId();

 printf("Application started; primary ID is %d\n", dwParentID);
 
 //launch child thread
 hChildThread = CreateThread(
 0,
 0,
 ChildThreadFunc,
 (LPVOID)5,
 0,
 &dwChildID
 );

 //check if the thread was created
 if (hChildThread == INVALID_HANDLE_VALUE) {
 printf("Error creating thread.\n");
 exit(EXIT_FAILURE);
 }
 else {
 printf("Thread %d created.\n", dwChildID);
 }

 Sleep(5000);

 //get the exit code 
 GetExitCodeThread(hChildThread, &dwExitCode);

 printf("Child thread exited with value %d\n", dwExitCode);
 printf("Exiting main thread ID %d\n", dwParentID);

 //close the handle
 CloseHandle(hChildThread);

 return 0;
}

Note that is possible to make the current thread stop executing for a specified period of time by calling the Sleep() function. In our second program, we will code a simple thread that beeps in the background.

#include "stdafx.h"
#include <Windows.h>

int GetInput(TCHAR tszPtr[], int Buffer_Size);
int ConvertTCHARToInt(TCHAR *tszPtr);
DWORD WINAPI BeepThread(LPVOID ptrParam);

struct SBeep {
 int iIterations;
 int iFrequency;
 int iDuration;
};

int main()
{
 HANDLE hThread;
 DWORD dwThreadID;
 struct SBeep structBeep;
 const int Buffer_Size = 56;
 TCHAR tszBuffer[Buffer_Size];

 printf("Please enter the number of beeps you'd like us to produce!\n");
 GetInput(tszBuffer, Buffer_Size);
 structBeep.iIterations = ConvertTCHARToInt(tszBuffer);
 printf("Please enter the frequency you'd like us to produce!\n");
 GetInput(tszBuffer, Buffer_Size);
 structBeep.iFrequency = ConvertTCHARToInt(tszBuffer);
 printf("Please enter the duration of the beep!\n");
 GetInput(tszBuffer, Buffer_Size);
 structBeep.iDuration = ConvertTCHARToInt(tszBuffer);
 _tprintf(_T("We will beep %d times for you at %d frequency for %d duration.\n"), 
 structBeep.iIterations, structBeep.iFrequency, structBeep.iDuration);
 //create a thread to execute the BeepThread function
 hThread = CreateThread(0, 0, BeepThread, &structBeep, 0, &dwThreadID);

 //wait for the thread to finish
 while (WaitForSingleObject(hThread, 500) == WAIT_TIMEOUT) {
 printf("Waiting on thread %d.\n", dwThreadID);
 }

 printf("All done!\n");

 return 0;
}

//get the string input 
int GetInput(TCHAR tszPtr[], int Buffer_Size) {
 TCHAR tch;
 int iCount = 0;
 while ((iCount < Buffer_Size) && (tch = getchar())!='\n'){
 *(tszPtr + iCount++) = tch;
 }
 //null-terminate the string
 *(tszPtr + iCount) = '\0';
 return iCount;
}

int ConvertTCHARToInt(TCHAR *tszPtr){
 int iCount = 0;
 int iRVal = 0;
 int iPlace = 1;
 //get length of string
 while (*(tszPtr + iCount) != '\0') { iCount++; }
 //iterate backwards through string
 while (iCount-- > 0) {
 //to get numeric value of character, subtract character zero
 iRVal += (*(tszPtr + iCount) -'0') * iPlace;
 iPlace *= 10;
 }
 return iRVal;
}

//the function to run in the thread
DWORD WINAPI BeepThread(LPVOID ptrParam) {
 //cast LPVOID param to correct data type
 struct SBeep *structBeep = (SBeep*)ptrParam;
 while (structBeep->iIterations-- > 0) {
 Beep(structBeep->iFrequency, structBeep->iDuration);
 Sleep(500);
 }
 return 0;
}

This program makes use of the WaitForSingleObject() function. We pass in as timeout value 200 milliseconds. If the thread has not completed by the time at which the timeout value has been reached, it returns the value WAIT_TIMEOUT.

As we have seen, it is possible to pass in a parameter to the thread function in the form of a pointer to a structure.

#include "stdafx.h"
#include <Windows.h>

int GetInput(TCHAR tszPtr[], int Buffer_Size);
int ConvertTCHARToInt(TCHAR *tszPtr);
DWORD WINAPI BeepThread(LPVOID ptrParam);
VOID BeepForUs(int iIter, int iFreq, int iDur, int iPause);

struct SBeep {
 int iIterations;
 int iFrequency;
 int iDuration;
 //new field, pause
 int iPause;
};

int main()
{
 const int Buffer_Size = 56;
 const int Num_Threads = 5;

 struct SBeep structBeep;

 TCHAR tszBuffer[Buffer_Size];
 //we will have an array of thread handles
 HANDLE hThread[Num_Threads];

 DWORD dwThreadID;
 int iNumThreads = 0;

 do {
 printf("Please enter the number of beeps you'd like us to produce!\n");
 GetInput(tszBuffer, Buffer_Size);
 structBeep.iIterations = ConvertTCHARToInt(tszBuffer);
 printf("Please enter the frequency you'd like us to produce!\n");
 GetInput(tszBuffer, Buffer_Size);
 structBeep.iFrequency = ConvertTCHARToInt(tszBuffer);
 printf("Please enter the duration of the beep!\n");
 GetInput(tszBuffer, Buffer_Size);
 structBeep.iDuration = ConvertTCHARToInt(tszBuffer);
 printf("Please enter pause between beeps!\n");
 GetInput(tszBuffer, Buffer_Size);
 structBeep.iPause = ConvertTCHARToInt(tszBuffer);
 _tprintf(_T("We will beep %d times for you at %d frequency for %d duration and %d pause length.\n\n"),
 structBeep.iIterations, structBeep.iFrequency, structBeep.iDuration, structBeep.iPause);

 //create a thread and add it to the array
 hThread[iNumThreads] = CreateThread(0, 0, BeepThread, &structBeep, 0, &dwThreadID);
 } while (++iNumThreads < Num_Threads);

 //wait for the thread to finish
 while (WaitForSingleObject(hThread, 500) == WAIT_TIMEOUT) {
 printf("Waiting on thread %d.\n", dwThreadID);
 }

 //now we wait for all threads to finish executing
 WaitForMultipleObjects(Num_Threads, hThread, TRUE, INFINITE);

 printf("All done!\n");

 return 0;
}

//get the string input 
int GetInput(TCHAR tszPtr[], int Buffer_Size) {
 TCHAR tch;
 int iCount = 0;
 while ((iCount < Buffer_Size) && (tch = getchar())!='\n'){
 *(tszPtr + iCount++) = tch;
 }
 //null-terminate the string
 *(tszPtr + iCount) = '\0';
 return iCount;
}

int ConvertTCHARToInt(TCHAR *tszPtr){
 int iCount = 0;
 int iRVal = 0;
 int iPlace = 1;
 //get length of string
 while (*(tszPtr + iCount) != '\0') { iCount++; }
 //iterate backwards through string
 while (iCount-- > 0) {
 //to get numeric value of character, subtract character zero
 iRVal += (*(tszPtr + iCount) -'0') * iPlace;
 iPlace *= 10;
 }
 return iRVal;
}

//the function to run in the thread
DWORD WINAPI BeepThread(LPVOID ptrParam) {
 //make local copies of info
 struct SBeep * sBeep = (SBeep*)ptrParam;
 BeepForUs(sBeep->iIterations, sBeep->iFrequency, sBeep->iDuration, sBeep->iPause);
 return 0;
}

VOID BeepForUs(int iIter, int iFreq, int iDur, int iPause) {
 while (iIter-- > 0) {
 Beep(iFreq, iDur);
 Sleep(iPause);
 }
}

Thread prioritization is how important a thread is relative to others within the process. After we create a thread, we can change its priority from the default of normal to something more appropriate for the application. The SetThreadPriority() function takes two arguments; the first is the handle to the thread, the second is the priority value for the thread.

#include "stdafx.h"
#include <Windows.h>


int main()
{
 //thread priority named constants
 printf("THREAD_PRIORITY_TIME_CRITICAL = %d\n", THREAD_PRIORITY_TIME_CRITICAL);
 printf("THREAD_PRIORITY_HIGHEST = %d\n", THREAD_PRIORITY_HIGHEST);
 printf("THREAD_PRIORITY_ABOVE_NORMAL = %d\n", THREAD_PRIORITY_ABOVE_NORMAL);
 printf("THREAD_PRIORITY_NORMAL = %d\n", THREAD_PRIORITY_NORMAL);
 printf("THREAD_PRIORITY_BELOW_NORMAL = %d\n", THREAD_PRIORITY_BELOW_NORMAL);
 printf("THREAD_PRIORITY_LOWEST = %d\n", THREAD_PRIORITY_LOWEST);

 return 0;
}

In our next program, we will create five threads and assign them an arbitrary workload to accomplish. We will then randomly modify their priority and wait for all of them to complete. The threads should complete in order of priority, with the threads with a higher priority number finishing first.

#include "stdafx.h"
#include <Windows.h>
//for intializing srand()
#include <ctime>

DWORD WINAPI DoWork(LPVOID lpParam) {
 int i = INT_MAX;
 int j = 1;
 while (i-- > 0) { j = i - 1; }
 printf("Thread %d done.\n", GetCurrentThreadId());
 return 0;
}


int main()
{
 const int Num_Threads = 5;
 const int Num_Priorities = 5;
 HANDLE hThreads[Num_Threads];
 DWORD dwThreadIDs[Num_Threads];
 int iNamedCons[Num_Priorities] = { THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST };
 int iCounter;
 int iRandValue; 

 srand(time(NULL));

 for (iCounter = 0; iCounter < Num_Threads; iCounter++) {
 hThreads[iCounter] = CreateThread(0, 0, DoWork, 0, 0, &dwThreadIDs[iCounter]);
 }

 //alter thread priority
 for (iCounter = 0; iCounter < Num_Threads; iCounter++) {
 iRandValue = rand() % Num_Priorities;
 printf("Setting thread ID %d to priority %d\n", dwThreadIDs[iCounter], iNamedCons[iRandValue]);
 SetThreadPriority(hThreads[iCounter], iNamedCons[iRandValue]);
 }

 WaitForMultipleObjects(Num_Threads, hThreads, TRUE, INFINITE);

 return 0;
}

Well, that is enough for today. Take a look at my book, if you are interested: http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s