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/