C++ Multithreading

Introduction

Multithreading in C++ allows a program to run multiple threads concurrently. This can significantly improve performance, particularly on multicore processors, by allowing different parts of a program to execute simultaneously. C++11 introduced a standardized way to handle threads via the <thread> library, providing robust tools to create, manage, and synchronize threads.

Basic Concepts

  • Thread: A thread is the smallest unit of processing that can be scheduled by the operating system.
  • Main Thread: The initial thread that starts executing when a C++ program begins.
  • Creating Threads: Threads can be created using the std::thread class.
  • Joining Threads: Threads can be synchronized using the join method to wait for them to complete.

Creating and Running Threads

Example: Basic Thread Creation

Let’s create a simple example where we launch a separate thread to perform a task.

#include <iostream>
#include <thread>

void printMessage(const std::string& message) {
    std::cout << message << std::endl;
}

int main() {
    // Create a thread
    std::thread t(printMessage, "Hello from thread!");

    // Wait for the thread to complete
    t.join();

    std::cout << "Hello from main!" << std::endl;

    return 0;
}

Output

Hello from thread!
Hello from main!

Explanation

  • The printMessage function prints a message to the console.
  • A std::thread object t is created, which starts executing the printMessage function in a separate thread.
  • The join method is called to wait for the thread t to complete before continuing in the main function.

Passing Arguments to Threads

You can pass arguments to the thread function using the std::thread constructor.

Example: Passing Arguments

#include <iostream>
#include <thread>

void printSum(int a, int b) {
    std::cout << "Sum: " << a + b << std::endl;
}

int main() {
    int x = 5;
    int y = 10;

    // Create a thread
    std::thread t(printSum, x, y);

    // Wait for the thread to complete
    t.join();

    return 0;
}

Output

Sum: 15

Explanation

  • The printSum function takes two integers as arguments and prints their sum.
  • A std::thread object t is created, which starts executing the printSum function with the arguments x and y.
  • The join method is called to wait for the thread t to complete before continuing in the main function.

Detaching Threads

Detaching a thread allows it to run independently from the main thread. The main thread will not wait for the detached thread to finish.

Example: Detaching a Thread

#include <iostream>
#include <thread>

void printMessage(const std::string& message) {
    std::cout << message << std::endl;
}

int main() {
    // Create and detach a thread
    std::thread t(printMessage, "Hello from detached thread!");
    t.detach();

    std::cout << "Hello from main!" << std::endl;

    // Allow time for the detached thread to finish
    std::this_thread::sleep_for(std::chrono::seconds(1));

    return 0;
}

Output

Hello from main!
Hello from detached thread!

Explanation

  • The printMessage function prints a message to the console.
  • A std::thread object t is created and detached, allowing it to run independently.
  • The main function uses std::this_thread::sleep_for to wait for the detached thread to finish executing.

Synchronization

Synchronization is crucial in multithreading to avoid data races and ensure data integrity. C++ provides several synchronization mechanisms, such as mutexes and condition variables.

Example: Using Mutex for Synchronization

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;

void printMessage(const std::string& message) {
    std::lock_guard<std::mutex> lock(mtx);
    std::cout << message << std::endl;
}

int main() {
    std::thread t1(printMessage, "Hello from thread 1!");
    std::thread t2(printMessage, "Hello from thread 2!");

    t1.join();
    t2.join();

    return 0;
}

Output

Hello from thread 1!
Hello from thread 2!

Explanation

  • The std::mutex object mtx is used to synchronize access to the console.
  • The printMessage function uses a std::lock_guard to lock the mutex while printing the message.
  • Two threads, t1 and t2, are created and joined, ensuring synchronized access to the console.

Practical Example: Multithreaded Calculation

Let’s create a more practical example where multiple threads calculate the sum of different parts of an array.

Code Example

#include <iostream>
#include <thread>
#include <vector>

void partialSum(const std::vector<int>& arr, int start, int end, int& result) {
    result = 0;
    for (int i = start; i < end; ++i) {
        result += arr[i];
    }
}

int main() {
    std::vector<int> arr(100, 1); // Array with 100 elements, all set to 1
    int result1 = 0, result2 = 0;

    // Create two threads to calculate partial sums
    std::thread t1(partialSum, std::ref(arr), 0, arr.size() / 2, std::ref(result1));
    std::thread t2(partialSum, std::ref(arr), arr.size() / 2, arr.size(), std::ref(result2));

    t1.join();
    t2.join();

    int totalSum = result1 + result2;
    std::cout << "Total sum: " << totalSum << std::endl;

    return 0;
}

Output

Total sum: 100

Explanation

  • The partialSum function calculates the sum of a portion of the array.
  • Two threads, t1 and t2, are created to calculate the sums of different parts of the array concurrently.
  • The main function waits for both threads to complete and then adds their results to get the total sum.

Conclusion

Multithreading in C++ allows you to run multiple threads concurrently, improving performance and responsiveness. The std::thread class provides a simple and effective way to create and manage threads. Synchronization mechanisms like mutexes ensure data integrity in multithreaded programs. Understanding and effectively using multithreading is essential for writing high-performance and reliable C++ programs.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top