BogoToBogo
  • Home
  • About
  • Big Data
  • Machine Learning
  • AngularJS
  • Python
  • C++
  • go
  • DevOps
  • Kubernetes
  • Algorithms
  • More...
    • Qt 5
    • Linux
    • FFmpeg
    • Matlab
    • Django 1.8
    • Ruby On Rails
    • HTML5 & CSS

C++ Tutorial Multi-Threaded Programming II C++ Thread for Win32 - 2020

cplusplus_icon.png




Bookmark and Share





bogotobogo.com site search:




Multi-Threaded Programming II

Multithreaded programs are most of the cases using Java Threading, the POSIX PThreads library, and the Windows API.


In this tutorial on multithreaded, we'll make C++ Thread class hiding the details of thread creation in Pthreads/Win32.





MyungSungSan4

Concurrent Programming

When two or more threads are programmed to be executed concurrently and to work together to perform some task, we call it concurrent program. The OS manages the usage of resources by the program.

Java provides a Thread class and Win32 and Pthreads provide a set of function calls for creating and manipulating threads.

All concurrent programs exhibit behavior that is unpredictable. This creates plethora of challenges for programmers when they write concurrent programs.




C++ Class Thread for Win32
#include <iostream>
#include <memory>
#include <cassert>
#include <windows.h>
#include <process.h>

class Runnable {
public:
	virtual void* run() = 0;
	virtual ~Runnable() = 0;
};

// Pure virtual destructor: function body required
Runnable::~Runnable(){};

class Thread {
public:
	Thread(std::auto_ptr<Runnable> run);
	Thread();
	virtual ~Thread();
	void start();
	void* join();
private:
	HANDLE hThread;
	unsigned wThreadID;
	// runnable object will be deleted automatically
	std::auto_ptr<Runnable> runnable;
	Thread(const Thread&);
	const Thread& operator=(const Thread&);
	// called when run() completes
	void setCompleted();
	// stores return value from run()
	void* result;
	virtual void* run() {return 0;}
	static unsigned WINAPI startThreadRunnable(LPVOID pVoid);
	static unsigned WINAPI startThread(LPVOID pVoid);
	void printError(LPTSTR lpszFunction, LPSTR fileName, int lineNumber);
};

Thread::Thread(std::auto_ptr<Runnable> r) : runnable(r) {
	if(!runnable.get())
		printError("Thread(std::auto_ptr<Runnable> r) failed at ",
				__FILE__, __LINE__);
	hThread = 
		(HANDLE)_beginthreadex(NULL,0,Thread::startThreadRunnable,
				(LPVOID)this, CREATE_SUSPENDED, &wThreadID;);
	if(!hThread)
		printError("_beginthreadex failed at ",__FILE__, __LINE__);
}

Thread::Thread() : runnable(NULL) {
	hThread = 
		(HANDLE)_beginthreadex(NULL,0,Thread::startThread,
				(LPVOID)this, CREATE_SUSPENDED, &wThreadID;);
	if(!hThread)
		printError("_beginthreadex failed at ",__FILE__, __LINE__);
}

unsigned WINAPI Thread::startThreadRunnable(LPVOID pVoid) {
	Thread* runnableThread = static_cast<Thread*>(pVoid);
	runnableThread->result = runnableThread->runnable->run();
	runnableThread->setCompleted();
	return reinterpret_cast<unsigned>(runnableThread->result);
}

unsigned WINAPI Thread::startThread(LPVOID pVoid) {
	Thread* aThread = static_cast<Thread*>(pVoid);
	aThread->result = aThread->run();
	aThread->setCompleted();
	return reinterpret_cast<unsigned>(aThread->result);
}

Thread::~Thread() {
	if(wThreadID != GetCurrentThreadId()) {
		DWORD rc = CloseHandle(hThread);
		if(!rc) printError
			("CloseHandle failed at ",__FILE__, __LINE__);
	}
}

void Thread::start() {
	assert(hThread);
	DWORD rc = ResumeThread(hThread);
	// thread created is in suspended state, 
	// so this starts it running
	if(!rc) printError
			("ResumeThread failed at ",__FILE__, __LINE__);
}

void* Thread::join() {
	// A thread calling T.join() waits until thread T completes.
	return result;
}

void Thread::setCompleted() {
	// Notify any threads that are waiting in join()
}

void Thread::printError(LPSTR lpszFunction, LPSTR fileName, int lineNumber)
{
	TCHAR szBuf[256];
	LPSTR lpErrorBuf;
	DWORD errorCode=GetLastError();
	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER||
		FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		errorCode,
		MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
		(LPTSTR)&lpErrorBuf;,
		0,
		NULL);
	wsprintf(szBuf,"%s failed at line %d in %s with error %d: %s", 
		     lpszFunction, lineNumber, fileName, errorCode, lpErrorBuf);
	DWORD numWritten; 
	WriteFile(GetStdHandle(STD_ERROR_HANDLE),
		szBuf,
		strlen(reinterpret_cast <const char *> (szBuf)),
		&numWritten;,
		FALSE);
	LocalFree(lpErrorBuf);
	exit(errorCode);
}

class simpleRunnable: public Runnable {
public:
	simpleRunnable(int ID) : myID(ID) {}
	virtual void* run() {
		std::cout << "Thread " << myID << " is running" << std::endl;
		return reinterpret_cast<void*>(myID);
	}
private:
	int myID;
};

class simpleThread: public Thread {
public:
	simpleThread(int ID) : myID(ID) {}
	virtual void* run() {
		std::cout << "Thread " << myID << " is running" << std::endl;
		return reinterpret_cast<void*>(myID);
	}
private:
	int myID;
};

int main() {
	// thread1 and thread2 are created on the heap
	// thread3 is created on the stack
	// The destructor for thread1 and thread2 will automatically 
	// delete the thread objects.
	std::auto_ptr<Runnable> r(new simpleRunnable(1));
	std::auto_ptr<Thread> thread1(new Thread(r));
	thread1->start();
	std::auto_ptr<simpleThread> thread2(new simpleThread(2));
	thread2->start();
	simpleThread thread3(3);
	thread3.start();
	// wait for the threads to finish
	int result1 = reinterpret_cast<int>(thread1->join());
	int result2 = reinterpret_cast<int>(thread2->join());
	int result3 = reinterpret_cast<int>(thread3.join());
	std::cout << result1 << ' ' << result2 << ' ' << result3 
		<< std::endl;
	return 0;
}


MyungSungSan3

Let's look at the code in detail.

  1. We have two major C++ classes: Runnable and Thread.
  2. The run() method is returning a value utilizing the fact that Win32 thread functions can return a value.
  3. The return value can be retrieved by using a Pthread-style call to method join().
  4. Class Runnable mimics Java's Runnable interface.
  5. We created threads in the same way as Java did: we write a C++ class that provides a run() method and inherits from Runnable.
    class simpleRunnable: public Runnable {
    public:
    	...
    	virtual void* run() {
    	...
    };
    
  6. We created an instance of this class.
    std::auto_ptr<Runnable> r(new simpleRunnable(1));
    
  7. Then, pass a pointer to that instance as an argument to the Thread class constructor.
    std::auto_ptr<Thread> thread1(new Thread(r));
    
  8. We call start() on that Thread object.
    thread1->start();
    
  9. Class Thread also provides a join() method that simulates the pthread_join() operation.
  10. A call to T.join() blocks the caller until thread T's run() method completes.
  11. We use T.join() to ensure that T's run() method is completed before Thread T is destructed and the main thread completes.
  12. Method join() returns the value that we returned by run().
  13. Method join() is useful in Java when one threads needs to make sure that other threads have completed before accessing their results.
  14. But Java's run() method cannot return a value, so results must be obtained some other way.
  15. The implementation is not simple. When a C++ Thread is created, the corresponding Thread constructor calls function _beginthreadex()
  16. The _beginthreadex() with the following arguments:
    1. NULL
      This is the default value for security attributes.
    2. 0
      This is the default value for stack size.
    3. Thread::startThread() or Thread::startThreadRunnable()
      Method startThread() is the startup method for threads created by inheriting from class Thread. Method startThreadRunnable() is the startup method for threads created from Runnable object.
    4. (LPVOID)this
      The fourth argument is a pointer to this Thread object, which is passed through to method startThread() or startThreadRunnable(). Thus, all threads execute one of the startup methods, but the startup methods receive a different Thread pointer each time they are executed.
    5. CREATED_SUSPENDED
      A Win32 thread is created to execute the startup method, but this thread is created in suspended mode, so the startup method does not begin executing until start() is called on the thread.
      thread1->start();
      
  17. The thread is not actually started until method Thread::start() calls Win32 function ResumeThread(), which allows the thread to be scheduled and the startup method to begin execution.
    void Thread::start() {
    	assert(hThread);
    	DWORD rc = ResumeThread(hThread);
    	// thread created is in suspended state, 
    	// so this starts it running
    }
    
  18. The startup method is either startThread() or startThreadRunnable() depending on which Thread constructor was used to create the Thread object.
  19. Method startThread() casts its void* pointer to Thread*.
    Thread* aThread = static_cast<Thread*>(pVoid);
    
  20. Then, it calls the run() method of its Thread* parameter.
    aThread->result = aThread->run();
    
  21. When the run() method returns, startThread() calls setCompleted() to set the thread's status to completed and to notify any threads waiting in join() that the thread has completed.
    aThread->setCompleted();
    
  22. The return value of the run() method is saved so that it can be retrieved in method join().
  23. Static method startThreadRunnable() performs similar steps when thread are created from Runnable objects.
  24. We use auto_ptr<> objects to manage the destruction of two of the threads and the Runnable object r.
    std::auto_ptr<Runnable> r(new simpleRunnable(1));
    std::auto_ptr<Thread> thread1(new Thread(r));
    ...
    std::auto_ptr<simpleThread> thread2(new simpleThread(2));
    
  25. When auto_ptr<> objects thread1 and thread2 are destroyed automatically at the end of the program, their destructors will invoke delete automatically on the pointers with which they were initialized.
  26. Passing auto_ptr<Runnable> object to the Thread class constructor passes ownership of the Runnable object from the main thread to the child thread.
  27. The auto_ptr<Runnable> object in the child thread that receives the auto_ptr<Runnable> object owns the Runnable object that it has a pointer to, and will automatically delete the pointed-to object when the child thread is destroyed.
  28. When ownership is passed to the thread, the auto_ptr<Runnable> object in main is set automatically to a null state and can no longer be used to refer to the Runnable object. This protects against double deletion by the child thread and the main thread. It also prevents main from deleting the Runnable object before the thread has completed method run() and from accessing the Runnable object while the thread is accessing it.
  29. The startup functions, startThreadRunnable() and startThread() are static member functions. This is because the _beginthreadex() expects to receive the address of a startup function that has a single (void*) parameter.
    unsigned WINAPI Thread::startThread(LPVOID pVoid) {
    	Thread* aThread = static_cast<Thread*>(pVoid);
    	....
    }
    
    A nonstatic member function that declares a single parameter actually has two parameters because of this pointer. So, if the startup function is a nonstatic member function, the hidden parameter, this, gets in the way and the call to the startup function fails. But the static member functions do not have this hidden parameter.
  30. The results of the run are quite unpredictable as we predicted.

    run1

    run2

    run3

    run4

    run5

note: This tutorial on Multithreads for Win32 is largely based on the book "Modern Multithreading" by Carver and Tai. Here, I converted it more reader friendly.

Other Threading Tutorials:

  1. Multi-Threaded Programming - Terminology - Semaphore, Mutex, Priority Inversion etc.
  2. Multi-Threaded Programming II - Native Thread for Win32 (A)
  3. Multi-Threaded Programming II - Native Thread for Win32 (B)
  4. Multi-Threaded Programming II - Native Thread for Win32 (C)
  5. Multi-Threaded Programming II - C++ Thread for Win32
  6. Multi-Threaded Programming III - C/C++ Class Thread for Pthreads
  7. MultiThreading/Parallel Programming - IPC
  8. Multi-Threaded Programming with C++11 Part A (start, join(), detach(), and ownership)
  9. Multi-Threaded Programming with C++11 Part B (Sharing Data - mutex, and race conditions, and deadlock)
  10. Multithread Debugging


Threading with QT5:

  1. QThreads - Introduction
  2. QThreads - Creating Threads
  3. Creating QThreads using QtConcurrent
  4. QThreads - Priority
  5. QThreads - QMutex
  6. QThreads - GuiThread
  7. QHttp - Downloading Files
  8. QNetworkAccessManager and QNetworkRequest - Downloading Files
  9. QUdpSocket
  10. QTcpSocket
  11. QTcpSocket with Signals and Slots
  12. QTcpServer - Client and Server
  13. QTcpServer - Loopback Dialog
  14. QTcpServer - Client and Server using MultiThreading
  15. QTcpServer - Client and Server using QThreadPool
  16. Asynchronous QTcpServer - Client and Server using QThreadPool




Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization

YouTubeMy YouTube channel

Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






C++ Tutorials

C++ Home

Algorithms & Data Structures in C++ ...

Application (UI) - using Windows Forms (Visual Studio 2013/2012)

auto_ptr

Binary Tree Example Code

Blackjack with Qt

Boost - shared_ptr, weak_ptr, mpl, lambda, etc.

Boost.Asio (Socket Programming - Asynchronous TCP/IP)...

Classes and Structs

Constructor

C++11(C++0x): rvalue references, move constructor, and lambda, etc.

C++ API Testing

C++ Keywords - const, volatile, etc.

Debugging Crash & Memory Leak

Design Patterns in C++ ...

Dynamic Cast Operator

Eclipse CDT / JNI (Java Native Interface) / MinGW

Embedded Systems Programming I - Introduction

Embedded Systems Programming II - gcc ARM Toolchain and Simple Code on Ubuntu and Fedora

Embedded Systems Programming III - Eclipse CDT Plugin for gcc ARM Toolchain

Exceptions

Friend Functions and Friend Classes

fstream: input & output

Function Overloading

Functors (Function Objects) I - Introduction

Functors (Function Objects) II - Converting function to functor

Functors (Function Objects) - General



Git and GitHub Express...

GTest (Google Unit Test) with Visual Studio 2012

Inheritance & Virtual Inheritance (multiple inheritance)

Libraries - Static, Shared (Dynamic)

Linked List Basics

Linked List Examples

make & CMake

make (gnu)

Memory Allocation

Multi-Threaded Programming - Terminology - Semaphore, Mutex, Priority Inversion etc.

Multi-Threaded Programming II - Native Thread for Win32 (A)

Multi-Threaded Programming II - Native Thread for Win32 (B)

Multi-Threaded Programming II - Native Thread for Win32 (C)

Multi-Threaded Programming II - C++ Thread for Win32

Multi-Threaded Programming III - C/C++ Class Thread for Pthreads

MultiThreading/Parallel Programming - IPC

Multi-Threaded Programming with C++11 Part A (start, join(), detach(), and ownership)

Multi-Threaded Programming with C++11 Part B (Sharing Data - mutex, and race conditions, and deadlock)

Multithread Debugging

Object Returning

Object Slicing and Virtual Table

OpenCV with C++

Operator Overloading I

Operator Overloading II - self assignment

Pass by Value vs. Pass by Reference

Pointers

Pointers II - void pointers & arrays

Pointers III - pointer to function & multi-dimensional arrays

Preprocessor - Macro

Private Inheritance

Python & C++ with SIP

(Pseudo)-random numbers in C++

References for Built-in Types

Socket - Server & Client

Socket - Server & Client 2

Socket - Server & Client 3

Socket - Server & Client with Qt (Asynchronous / Multithreading / ThreadPool etc.)

Stack Unwinding

Standard Template Library (STL) I - Vector & List

Standard Template Library (STL) II - Maps

Standard Template Library (STL) II - unordered_map

Standard Template Library (STL) II - Sets

Standard Template Library (STL) III - Iterators

Standard Template Library (STL) IV - Algorithms

Standard Template Library (STL) V - Function Objects

Static Variables and Static Class Members

String

String II - sstream etc.

Taste of Assembly

Templates

Template Specialization

Template Specialization - Traits

Template Implementation & Compiler (.h or .cpp?)

The this Pointer

Type Cast Operators

Upcasting and Downcasting

Virtual Destructor & boost::shared_ptr

Virtual Functions



Programming Questions and Solutions ↓

Strings and Arrays

Linked List

Recursion

Bit Manipulation

Small Programs (string, memory functions etc.)

Math & Probability

Multithreading

140 Questions by Google



Qt 5 EXPRESS...

Win32 DLL ...

Articles On C++

What's new in C++11...

C++11 Threads EXPRESS...

Go Tutorial

OpenCV...








Contact

BogoToBogo
contactus@bogotobogo.com

Follow Bogotobogo

About Us

contactus@bogotobogo.com

YouTubeMy YouTube channel
Pacific Ave, San Francisco, CA 94115

Pacific Ave, San Francisco, CA 94115

Copyright © 2024, bogotobogo
Design: Web Master