Not so experienced developers of Windows applications like me, always get confused in the first place when they think about the implementation of multi-threading. Not you? You might be expert though. My objective is to help novice programmers and the guys who don’t claim to be expert but do multi-threading when the requirement comes on the way. There are multiple options to implement multi-threading and thread synchronization in Windows. For unmanaged application you can either use C++ language library classes, Windows API or MFC classes and for Managed code you have to use .Net classes. To get confidence creating additional threads in your application you should have clear ideas of threads and processes in windows and how they work together. Let us first have a look on the basics of process and thread in Windows Operating System. Process and Threads in Windows In Windows every program runs under a windows process. Process has a process identifier, a virtual address space, executable code, data and resources (like files, kernel objects, input output devices etc), a security context, environment variables and at least one thread. A thread is a basic unit to which the operating system allocates it’s processor time. It is the basic entity within a process which can be scheduled for execution. A threads has a unique identifier, an entry point, exception handlers, thread local storage, scheduling priority and a thread context. The thread context includes CPU register values, kernel stack, user stack and an environment block in the address space of the process. Every process have at least one thread which is called the primary thread. While compiling and linking the code the entry point of the primary thread is decided. C++ linker looks for main() function and make it as the entry point of the executable. When the Operating System runs application the main thread gets the control of the application. Now main thread is free to create any number of threads limited by the resource availability in the system. Threads run independent of each other. This means one thread does not know that other threads are running. Also threads are preempted by OS and unpredictable. On the other hand threads of the same process share the virtual address, system resources, data and code of the process. Sharing code does not create any problem but sharing data and resources can create problem. For example if one thread is in the middle of modifying one piece of data and then another thread gets preempted which access that data, the second thread will get inconsistent value. Also there can be scenario where one thread need to wait for some action to be completed by another thread. So synchronization of resource access and communication between threads are essential part of a multi-threaded applications. A thread in Windows has some attributes which are decided during the creation and also can be altered before the thread start execution. When one thread creates another thread it specify those thread attributes or use the default attributes. The attributes include: Security Attributes: Controls the access to the thread object. It includes owner SID(Security Identifier), group SID, DACL (Discretionary Access Control List), SACL(System Access Control List) etc. Stack Size: Initial size of the thread stack Scheduling Priority: Priority of the thread like Critical or high priority. Thread priority is used by the OS thread scheduler. C++ interface for threading for Windows developers To implement the design requirement of multi-threading in Windows application using C++ language you have to use some libraries because the language itself does not know anything about threads. C++11 and later version added the threading support in the standard library. So if your code needs to be portable across platform and use default thread attributes you may prefer to use C++ standard library. On the other hand you may prefer to use Windows native support using Windows API if you need more control on thread attributes and you don’t want to depend on third party library or frame work. Microsoft provides MFC framework which is unmanged code that includes classes for Thread creation and management. These classes are basically object oriented wrapper of Windows API written in C++. If you are writing managed C++ code you have to use the .Net framework threading classes. Are you getting bored of long description? Want to jump to make your hands dirty with real code? Ok, the rest of the article will show you the executable code using different libraries. I have taken an example of a dummy application where there is a counter whose initial value is 0 and can be incremented upto a maximum value and can be decremented when it is grater than 0. It can be compared to an application which receive requests from different sources in parallel to store in a request queue and a request handler can process the requests in the queue. I kept the functionality of the examples exactly same while using different libraries or frameworks. So the output of all the programs should be same while running on Windows. In the examples I defined a thread safe class (MyCounter) which encapsulates the counter value and provides public methods for incrementing and decrementing the counter. Public methods are mutually exclusive, means only one thread can access the object at a time. When one thread is executing any public method all other threads trying to access the object will wait till the first thread releases the object. Then any one of the waiting threads will wake up and gain the access to the object. I am also creating two threads for incrementing the counter and one thread for decrementing the counter. The incrementing threads are elapsing a random amount of time between two increment operations. All the threads are accessing a single instance of the MyCounter class. There is a need for synchronization among threads; when an increment thread finds that the counter value has reached to the maximum allowed value it waits until counter value is decreased. Similarly when the decrement thread finds that the counter value is zero it goes into a waiting state until it’s counter value is increased. When the increment or decrement threads succeeds in it’s increment or decrement operation it generate the corresponding notification for waiting threads. Example 1: Use C++11 standard library concurrency classes Code: #include<iostream> #include<future> #include<mutex> using namespace std; class MyCounter { private: unsigned m_uCounter; // Counter value mutex m_mutexCounter;// mutex for exclusive access of the counter unsigned m_uMaxCount; // Maximum value allowed for the counter public: MyCounter(unsigned maxcount) :m_uMaxCount(maxcount),m_uCounter(0) { } bool Increment() { //Following line will lock the mutex object. Lock will be release in the destructor of the unique_lock object, it’s like smart pointer unique_lock<mutex> lockCounter(m_mutexCounter); cout << "+MyCounter::Increment(): Thread Id = "<<this_thread::get_id() << endl; bool bRet = false; //Increment only if the counter value did not reach to max value if (m_uCounter < m_uMaxCount) { cout << "Incrementing the counter value from "<< m_uCounter; m_uCounter++; cout <<" to "<<m_uCounter << endl; bRet = true; } else { cout << "Count is at the max limit, could not increment" << endl; } cout << "-MyCounter::Increment(): Thread Id = " << this_thread::get_id() << endl<<endl; //Mutex will be released here return bRet; } int Decrement() { //Following line will lock the mutex object. Lock will be release in the destructor of the unique_lock object, it’s like smart pointer unique_lock<mutex> lockCounter(m_mutexCounter); cout << "+MyCounter::Decrement(): Thread Id = " << this_thread::get_id() << endl; bool bRet = false; //Decrement only if it is greater than zero if (m_uCounter > 0) { cout << "Decrementing the counter value from " << m_uCounter; m_uCounter--; cout << " to " << m_uCounter << endl; bRet = true; } else { cout << "Count is at min value, Could not decrement" << endl; } cout << "-MyCounter::Decrement(): Thread Id = " << this_thread::get_id() << endl<<endl; //Mutex will be released here return bRet; } }; int main() { mutex MutexIncreased; // Used for attaching with condition variable mutex MutexDecreased;// Used for attaching with condition variable condition_variable CounterIncreased; // Used for Counter increased event condition_variable CounterDecreased; // Used for Counter increased event MyCounter counter(10); // Creating two lamdas: one for increment thread and one for decrement thread auto IncrementLamda = [&](unsigned itrcount) { for (unsigned i = 0; i < itrcount;i++) { while (!counter.Increment()) { //Wait for counter to be decremented CounterDecreased.wait(unique_lock<mutex>(MutexDecreased)); } // Wake up the threads waiting for counter increment CounterIncreased.notify_all(); std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 100 + 1)); } }; auto DecrementLamda = [&](unsigned itrcount) { for (unsigned i = 0; i < itrcount;i++) { while (!counter.Decrement()) { //Wait for counter to be decremented CounterIncreased.wait(unique_lock<mutex>(MutexIncreased)); } //Wake up the threads waiting for counter decrement CounterDecreased.notify_all(); std::this_thread::sleep_for(std::chrono::milliseconds(50)); } }; auto future1 = async(DecrementLamda, 25); auto future2 = async(IncrementLamda,10); auto future3 = async(IncrementLamda,15); future1.wait(); future2.wait(); future3.wait(); return 0; } In the above code I used lamda expression as thread entry point, condition variable for wait and notification, mutex for mutual exclusion and std::async() for asynchronous execution. You can use thread class instead of std::async(). Advantage of std::async() is that it can capture the return value of the thread function and catch the exception thrown by the thread function. Example 2: Use Windows threading API Code: #include<iostream> #include<windows.h> using namespace std; class MyCounter { private: unsigned m_uCounter; // Counter value HANDLE m_hMutex;// mutex for exclusive access of the counter unsigned m_uMaxCount; // Maximum value allowed for the counter public: MyCounter(unsigned maxcount) :m_uMaxCount(maxcount),m_uCounter(0) { // Create a unnamed non signaled mutex with default security attributes m_hMutex = CreateMutexW(NULL, FALSE, NULL); } bool Increment() { // Wait for the mutex with no timeout ::WaitForSingleObject(m_hMutex,INFINITE); cout << "+MyCounter::Increment(): Thread Id = "<< ::GetCurrentThreadId() << endl; bool bRet = false; //Increment only if the counter value did not reach to max value if (m_uCounter < m_uMaxCount) { cout << "Incrementing the counter value from "<< m_uCounter; m_uCounter++; cout <<" to "<<m_uCounter << endl; bRet = true; } else { cout << "Count is at the max limit, could not increment" << endl; } cout << "-MyCounter::Increment(): Thread Id = " << ::GetCurrentThreadId() << endl << endl; //Release the mutex ::ReleaseMutex(m_hMutex); return bRet; } int Decrement() { // Wait for the mutex with no timeout ::WaitForSingleObject(m_hMutex, INFINITE); cout << "+MyCounter::Decrement(): Thread Id = " << ::GetCurrentThreadId() << endl; bool bRet = false; //Decrement only if it is greater than zero if (m_uCounter > 0) { cout << "Decrementing the counter value from " << m_uCounter; m_uCounter--; cout << " to " << m_uCounter << endl; bRet = true; } else { cout << "Count is at min value, Could not decrement" << endl; } cout << "-MyCounter::Decrement(): Thread Id = " << ::GetCurrentThreadId() << endl << endl; //Release the Mutex ::ReleaseMutex(m_hMutex); return bRet; } }; // Creating global handles for events and the counter object HANDLE hEventInCreased; HANDLE hEventDeCreased; MyCounter counter(10); // Thread procedure DWORD WINAPI Increment(LPVOID lpParameter) { // The number of iteration is passed as thread function parameter unsigned *pitrcount = (unsigned*)lpParameter; unsigned itrcount = *pitrcount; for (unsigned i = 0; i < itrcount;i++) { while (!counter.Increment()) { //Wait for counter to be decremented ::WaitForSingleObject(hEventDeCreased, INFINITE); } // Wake up the threads waiting for counter increment ::SetEvent(hEventInCreased); ::Sleep(rand() % 100 + 1); } return 0; }; DWORD WINAPI Decrement(LPVOID lpParameter) { unsigned *pitrcount = (unsigned*)lpParameter; unsigned itrcount = *pitrcount; for (unsigned i = 0; i < itrcount;i++) { while (!counter.Decrement()) { //Wait for counter to be decremented ::WaitForSingleObject(hEventInCreased, INFINITE); } //Wake up the threads waiting for counter decrement ::SetEvent(hEventDeCreased); ::Sleep(50); } return 0; }; int main() { // Create unnamed, non-signaled, auto reset event with default security attributes hEventInCreased = CreateEvent(NULL, FALSE, FALSE, NULL); hEventDeCreased = CreateEvent(NULL, FALSE, FALSE, NULL); unsigned DecrementItr = 25; unsigned IncrementItr1 = 10; unsigned IncrementItr2 = 15; // Create threads with default security attributes and default stack size HANDLE hTh3 = ::CreateThread(NULL, 0, Decrement, &DecrementItr, 0, NULL); HANDLE hTh1 = ::CreateThread(NULL, 0, Increment, &IncrementItr1, 0, NULL); HANDLE hTh2 = ::CreateThread(NULL, 0, Increment, &IncrementItr2, 0, NULL); // Wait for thread to be terminated ::WaitForSingleObject(hTh1, INFINITE); ::WaitForSingleObject(hTh2, INFINITE); ::WaitForSingleObject(hTh3, INFINITE); return 0; } In the above code mutex is used for mutual exclusion and event is used for communication between threads when counter is changed Example 3: Use MFC threading classes Code: #include<iostream> #include <afxwin.h> #include<afxmt.h> using namespace std; class MyCounter { private: unsigned m_uCounter; // Counter value // Create a unnamed non signaled mutex with default secutity attributes CMutex m_Mutex; unsigned m_uMaxCount; // Maximum value allowed for the counter public: MyCounter(unsigned maxcount) :m_uMaxCount(maxcount),m_uCounter(0) { } bool Increment() { // Wait for the mutex with no timeout m_Mutex.Lock(); cout << "+MyCounter::Increment(): Thread Id = "<< ::GetCurrentThreadId() << endl; bool bRet = false; //Increment only if the counter value did not reach to max value if (m_uCounter < m_uMaxCount) { cout << "Incrementing the counter value from "<< m_uCounter; m_uCounter++; cout <<" to "<<m_uCounter << endl; bRet = true; } else { cout << "Count is at the max limit, could not increment" << endl; } cout << "-MyCounter::Increment(): Thread Id = " << ::GetCurrentThreadId() << endl << endl; m_Mutex.Unlock(); // Release the mutex return bRet; } int Decrement() { // Wait for the mutex with no timeout m_Mutex.Lock(); cout << "+MyCounter::Decrement(): Thread Id = " << ::GetCurrentThreadId() << endl; bool bRet = false; //Decrement the counter only if it is greater than zero if (m_uCounter > 0) { cout << "Decrementing the counter value from " << m_uCounter; m_uCounter--; cout << " to " << m_uCounter << endl; bRet = true; } else { cout << "Count is at min value, Could not decrement" << endl; } cout << "-MyCounter::Decrement(): Thread Id = " << ::GetCurrentThreadId() << endl << endl; m_Mutex.Unlock(); // Release the mutex return bRet; } }; // Create unnamed, non-signaled, auto reset event with default security attributes CEvent eIncreased; CEvent eDecresed; MyCounter counter(10); UINT __cdecl Increment(LPVOID lpParameter) { unsigned *pitrcount = (unsigned*)lpParameter; unsigned itrcount = *pitrcount; for (unsigned i = 0; i < itrcount;i++) { while (!counter.Increment()) { //Wait for counter to be decremented ::WaitForSingleObject(eDecresed.m_hObject, INFINITE); } // Wake up the threads waiting for counter increment eIncreased.SetEvent(); ::Sleep(rand() % 100 + 1); } return 0; }; UINT __cdecl Decrement(LPVOID lpParameter) { unsigned *pitrcount = (unsigned*)lpParameter; unsigned itrcount = *pitrcount; for (unsigned i = 0; i < itrcount;i++) { while (!counter.Decrement()) { //Wait for counter to be decremented ::WaitForSingleObject(eIncreased, INFINITE); } //Wake up the threads waiting for counter decrement eDecresed.SetEvent(); ::Sleep(50); } return 0; }; int main() { unsigned DecrementItr = 25; unsigned IncrementItr1 = 10; unsigned IncrementItr2 = 15; // Create threads with default security attributes and default stack size CWinThread* pThread1 = ::AfxBeginThread(Decrement, &DecrementItr,0,0,CREATE_SUSPENDED); CWinThread* pThread2 = ::AfxBeginThread(Increment, &IncrementItr1,0,0, CREATE_SUSPENDED); CWinThread* pThread3 = ::AfxBeginThread(Increment, &IncrementItr2, 0, 0, CREATE_SUSPENDED); pThread1->m_bAutoDelete = false; pThread2->m_bAutoDelete = false; pThread3->m_bAutoDelete = false; HANDLE hTh1 = pThread1->m_hThread; HANDLE hTh2 = pThread2->m_hThread; HANDLE hTh3 = pThread3->m_hThread; pThread1->ResumeThread(); pThread2->ResumeThread(); pThread3->ResumeThread(); // Wait for thread to be terminated ::WaitForSingleObject(hTh1, INFINITE); ::WaitForSingleObject(hTh2, INFINITE); ::WaitForSingleObject(hTh3, INFINITE); delete pThread1; delete pThread2; delete pThread3; return 0; } In the above program I used CMutex, CEvent and CWinThread classes. Example 4: Use .Net framework threading classes (For managed code) Code: #include<cstdlib> using namespace System; using namespace System::Threading; ref class MyCounter { private: unsigned m_uCounter; // Counter value // Declare an object for locking purpose Object^ lock; unsigned m_uMaxCount; // Maximum value allowed for the counter public: MyCounter(unsigned maxcount) :m_uMaxCount(maxcount), m_uCounter(0) { // Create the lock object lock = gcnew Object(); } bool Increment() { //Lock for exclusive access Monitor::Enter(lock); Console::WriteLine("+MyCounter::Increment(): Thread Name = " + Thread::CurrentThread->Name); bool bRet = false; //Increment only if the counter value did not reach to max value if (m_uCounter < m_uMaxCount) { Console::Write("Incrementing the counter value from "+ m_uCounter); m_uCounter++; Console::WriteLine(" to " + m_uCounter ); bRet = true; } else { Console::WriteLine( "Count is at the max limit, could not increment"); } Console::WriteLine("-MyCounter::Decrement(): Thread Name = " + Thread::CurrentThread->Name); Console::WriteLine(); // Release the lock Monitor::Exit(lock); return bRet; } bool Decrement() { // Wait for the mutex with no timeout Monitor::Enter(lock); Console::WriteLine( "+MyCounter::Decrement(): Thread Name = " + Thread::CurrentThread->Name); bool bRet = false; //Decrement only if it is greater than zero if (m_uCounter > 0) { Console::Write("Decrementing the counter value from "+ m_uCounter); m_uCounter--; Console::WriteLine(" to " + m_uCounter); bRet = true; } else { Console::WriteLine( "Count is at min value, Could not decrement"); } Console::WriteLine("-MyCounter::Decrement(): Thread Name = " + Thread::CurrentThread->Name); Console::WriteLine(); //Release the lock Monitor::Exit(lock); return bRet; } }; // Creating class containg the thread function public ref class ThreadFns { private: unsigned ItrCount; MyCounter^ counter; static AutoResetEvent^ Increased = gcnew AutoResetEvent(false); static AutoResetEvent^ Decreased = gcnew AutoResetEvent(false); public: ThreadFns(unsigned count, MyCounter^ C) { ItrCount = count; counter = C; } void Decrement() { for (unsigned i = 0; i < ItrCount;i++) { while (!counter->Decrement()) { //Wait for counter to be incremented Increased->WaitOne(); } //Wake up the threads waiting for counter decrement if (!Decreased->Set()) { Console::WriteLine(Thread::CurrentThread->Name + " Could not set the Increment event"); Console::WriteLine(); } Thread::Sleep(50); } }; void Increment() { for (unsigned i = 0; i < ItrCount;i++) { while (!counter->Increment()) { //Wait for counter to be decremented Decreased->WaitOne(); } //Wake up the threads waiting for counter increment if (!Increased->Set()) { Console::WriteLine(Thread::CurrentThread->Name + " Could not set the Increment event"); Console::WriteLine(); } Thread::Sleep(rand() % 100 + 1); } }; }; int main(array<System::String ^> ^args) { unsigned DecrementItr = 25; unsigned IncrementItr1 = 10; unsigned IncrementItr2 = 15; MyCounter^ Counter = gcnew MyCounter(10); // Create threads with default security attributes and default stack size ThreadFns^ thfn1 = gcnew ThreadFns(DecrementItr, Counter); ThreadFns^ thfn2 = gcnew ThreadFns(IncrementItr1, Counter); ThreadFns^ thfn3 = gcnew ThreadFns(IncrementItr2, Counter); Thread^ th1= gcnew Thread(gcnew ThreadStart(thfn1,&ThreadFns::Decrement)); Thread^ th2 = gcnew Thread(gcnew ThreadStart(thfn2, &ThreadFns::Increment)); Thread^ th3 = gcnew Thread(gcnew ThreadStart(thfn3, &ThreadFns::Increment)); th1->Name = "Decrement Thread"; th2->Name = "Increment Thread 1"; th3->Name = "Increment Thread 2"; th1->Start(); th2->Start(); th3->Start(); return 0; } In the above code thread function is encapsulated in a class. Monitor and Object classes are used for mutual execution. For event synchronization AutoResetEvent class is used. Programming with threads involves: Creating thread Setting the thread attributes like security parameters, stack size, thread priority etc. Starting the thread Stopping the thread Synchronization between threads when accessing shared resources Releasing the thread object Among above all the activities thread synchronization is the most crucial activity. The above example code is very simple scenario whereas the real life programs are much more complicated and will require much more design effort. Even in the above example a silly mistake can create a deadlock. In the example 4 in the class ThreadFns if you just remove the ‘static’ keyword from the line of code below, it will create a dead lock. Why? That is the home work... Code: static AutoResetEvent^ Increased = gcnew AutoResetEvent(false);