Multithreading is a powerful technique in programming that allows multiple threads to execute concurrently within a single program. This approach can significantly enhance the performance and responsiveness of applications, especially on multi-core systems. In this article, we will explore the fundamentals of multithreading in C++, focusing on POSIX threads (pthreads), and demonstrate how to create, manage, and utilize threads effectively.
What is a Thread?
A thread is a basic unit of CPU utilization, consisting of a program counter, a stack, and a set of registers. Threads within the same process share resources such as memory, file descriptors, and signal handlers. This lightweight nature of threads makes them ideal for performing multiple tasks concurrently within a single program.
In Linux, thread functions are declared in the <pthread.h>
header file. POSIX threads, or pthreads, provide a standard API for multithreading across UNIX-like operating systems, including Linux, macOS, FreeBSD, and OpenBSD.
Creating Threads in C++
Threads can be created using the pthread_create()
function. The syntax is as follows:
#include <iostream>
#include <pthread.h>
using namespace std;
void* print_message(void* message) {
cout << "Thread message: " << (char*)message << endl;
return nullptr;
}
int main() {
pthread_t thread;
const char* message = "Hello from the thread!";
pthread_create(&thread, NULL, print_message, (void*)message);
pthread_join(thread, NULL);
cout << "Main thread finished execution" << endl;
return 0;
}
Output:
Thread message: Hello from the thread!
Main thread finished execution
Terminating Threads in C++
Threads can be terminated using the pthread_exit()
function. This is typically called when a thread completes its work.
pthread_exit(NULL);
Joining and Detaching Threads
Joining Threads
The pthread_join()
function allows the main thread to wait for the termination of a child thread. This ensures that the main thread does not terminate before its child threads.
pthread_join(thread, NULL);
#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
void* print_message(void*) {
sleep(1);
cout << "Child thread executed" << endl;
return nullptr;
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, print_message, NULL);
pthread_join(thread, NULL);
cout << "Main thread finished execution" << endl;
return 0;
}
Output:
Child thread executed
Main thread finished execution
Detaching Threads
The pthread_detach()
function allows a thread to execute independently of the main thread, meaning the main thread does not need to wait for it to finish.
pthread_detach(thread);
#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
void* print_message(void*) {
sleep(1);
cout << "Child thread executed independently" << endl;
return nullptr;
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, print_message, NULL);
pthread_detach(thread);
cout << "Main thread finished execution" << endl;
return 0;
}
Main thread finished execution
Child thread executed independently
Passing Arguments to Threads
Multiple arguments can be passed to a thread using a structure. This allows complex data types to be handled within the thread’s callback function.
Example: Passing Arguments to Threads
#include <iostream>
#include <pthread.h>
using namespace std;
struct ThreadData {
int thread_id;
const char* message;
};
void* print_data(void* thread_arg) {
ThreadData* data = (ThreadData*)thread_arg;
cout << "Thread ID: " << data->thread_id << ", Message: " << data->message << endl;
return nullptr;
}
int main() {
pthread_t threads[3];
ThreadData thread_data[3];
for (int i = 0; i < 3; ++i) {
thread_data[i].thread_id = i;
thread_data[i].message = "Hello from the thread!";
pthread_create(&threads[i], NULL, print_data, (void*)&thread_data[i]);
}
for (int i = 0; i < 3; ++i) {
pthread_join(threads[i], NULL);
}
return 0;
}
Thread ID: 0, Message: Hello from the thread!
Thread ID: 1, Message: Hello from the thread!
Thread ID: 2, Message: Hello from the thread!
Advantages of Multithreading in C++
- Improved Performance: On multi-core systems, multithreading can significantly enhance performance by allowing multiple threads to run in parallel.
- Concurrency: Multithreading allows multiple tasks to be performed simultaneously, improving the responsiveness and efficiency of applications.
- Resource Sharing: Threads within the same process share memory and resources, leading to efficient communication and data sharing.
- Better CPU Utilization: Multithreading enables programs to make optimal use of available CPU resources.