You are currently viewing C++: How to Use Threads for Parallel Programming

C++: How to Use Threads for Parallel Programming

Parallel programming has become increasingly important in modern software development as it allows us to leverage the power of multi-core processors to execute tasks concurrently. C++ provides robust support for parallel programming through its threading library. By utilizing threads, developers can design efficient and responsive applications that can perform multiple tasks simultaneously. In this blog post, we will explore the basics of using threads in C++ for parallel programming and provide code examples to illustrate the concepts.

Understanding Threads

A thread can be thought of as an independent sequence of instructions that can execute concurrently with other threads within a process. Each thread has its own program counter, stack, and local variables, but shares the same memory space with other threads in the process. This shared memory enables communication and synchronization between threads.

Creating Threads in C++

C++ provides a threading library called that offers the necessary functionality to work with threads. To create a new thread, you need to define a function or a callable object that represents the task to be executed concurrently. The std::thread class is used to create and manage threads. Here’s an example:

#include <iostream>
#include <thread>

// Function to be executed by the thread
void task() {

    std::cout << "This is executed in a separate thread!" << std::endl;
    
}

int main() {

    // Create a thread and execute the task
    std::thread t{task};

    // Wait for the thread to finish
    t.join();
    
    // Continue with the main thread
    std::cout << "Back in the main thread!" << std::endl;

    return 0;

}

The code above defines a task() that prints a message. We then create a std::thread object called t and pass task as an argument. The join() function is used to wait for the thread to complete before continuing with the main thread.

Passing Arguments to Thread Functions

More often than not, you need to pass arguments to the thread function. This can be done by passing them as additional arguments to the std::thread constructor. Here’s an example:

#include <iostream>
#include <thread>

// Function to be executed by the thread
void greet(const std::string& name) {

    std::cout << "Hello, " << name << "!" << std::endl;

}

int main() {
    
    std::string name = "Edward Nyirenda";

    std::thread t{greet, name};

    t.join();

    return 0;

}

In this example, we pass the name variable as an argument to the greet() function. The std::string is passed by reference to avoid unnecessary copying.

Thread Synchronization

When working with multiple threads, it’s essential to ensure proper synchronization to avoid data races and ensure the correctness of shared resources. C++ provides several synchronization mechanisms, such as mutexes, condition variables, and atomic operations, to coordinate access to shared data. Let’s look at an example that demonstrates the use of a mutex:

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

// Mutex for protecting the shared resource
std::mutex mtx;

// Function to be executed by the thread
void printNumber(int number) {

    // Acquire the lock
    mtx.lock();

    std::cout << number << std::endl;

    // Release the lock
    mtx.unlock();

}

int main() {

    std::thread t1{printNumber, 1};
    std::thread t2{printNumber, 2};

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

    return 0;

}

In this example, we create a mutex (mtx) that will protect the shared resource, which is the std::cout stream. The lock() and unlock() functions are used to acquire and release the lock, respectively, ensuring that only one thread can access the shared resource at a time, one thread can print at a time. If it was a variable, it would be to ensure only one thread can change or access it at a time.

Conclusion

Parallel programming with threads in C++ allows developers to utilize the full potential of modern hardware by executing tasks concurrently. By creating multiple threads and properly synchronizing their execution, we can design efficient and responsive applications. We covered the basics of using threads in C++, including thread creation, passing arguments to thread functions, and thread synchronization using a mutex. With this knowledge, you can start exploring the world of parallel programming in C++ and build high-performance applications.

Remember, when working with threads, it’s crucial to consider thread safety, avoid data races, and properly synchronize shared resources to ensure the correctness of your program. Here are some additional resources I recommend and thought you might find helpful:

I hope you found this information informative. If you would like to receive more content, please consider subscribing to our newsletter!

Leave a Reply