C++ Exercise 2: What is Concurrency and Why is it Important?

C++ Exercise 2: What is Concurrency and Why is it Important?

Concurrency in programming refers to the ability of a system to execute multiple tasks or processes simultaneously. In C++, concurrency is achieved using threads, which allow for the parallel execution of tasks, improving efficiency, resource utilization, and performance, especially on multicore processors.

Concurrency is essential in modern software development because most applications require some level of multitasking, such as handling multiple users in a web application, performing background computations, or responding to real-time events. Efficiently managing these tasks can make the difference between a slow, unresponsive program and a fast, scalable one.

In C++, concurrency is facilitated through the <thread> library, which provides a set of classes and functions to create and manage threads. However, concurrency also introduces complexities, such as race conditions, where multiple threads try to modify shared data simultaneously, potentially leading to inconsistent or incorrect results. Let's explore this concept with an example.

Example Code: Visualizing Concurrency Issues

Below is a simple example in C++ that demonstrates concurrency:

Example on thread

Explanation of the Code

  • Shared Counter: The variable counter is shared between two threads, both of which attempt to increment its value in parallel.
  • Loops: The number of times each thread increments the counter is determined by the input passed to the program.
  • Thread Creation: Two threads (t1 and t2) are created, both running the same worker function.
  • Concurrency Issue: Both threads try to increment the shared counter variable simultaneously. Since the increment operation (++counter) is not atomic, this can lead to race conditions, where both threads try to read and update the counter at the same time, potentially causing incorrect final values.

Visualizing the Concurrency Issue

Instead of running or build and run the programme, run the below command to compile the code:


Compile the code using syntax

Note:

  • g++ is the GNU C++ Compiler, part of the GNU Compiler Collection (GCC). It is used to compile C++ source files into executable programs.
  • -o myprogram: Specifies that the output executable should be named myprogram. In this case thread.
  • myprogram.cpp: The source file to be compiled. In above case threads.cpp
  • -Wall: Enables all standard warnings to help catch potential issues in the code.
  • The -pthread flag is necessary to link the pthread library, which is used for threading.

Finally, you can see the executable in the same folder. In above case thread.exe

To understand this, imagine both threads as workers trying to add a value to a shared total. Without proper coordination, they may overwrite each other's work, leading to inconsistencies in the final result.

For instance, if loops is set to 1000, each thread should increment the counter by 1000, resulting in a final value of 2000.

thread loop with 10000

Now, if loops is set to 100000, each thread should increment the counter by 100000, resulting in a final value of 200000.

However, due to race conditions, the final result might be less than 200000 because both threads might read the same value of counter and then overwrite it before the other can make its change. Due to concurrency issues, the result might be lower:


sample output


This inconsistency occurs because both threads are incrementing the counter without any synchronization, causing some increments to be lost.

Solving Concurrency Issues: std::atomic and std::mutex

To solve this problem, we need to ensure that the counter is incremented atomically or protect the increment operation with a mutex.

1. Using std::atomic

The simplest way to make the counter thread-safe is to use std::atomic, which ensures that operations on the counter are performed atomically (i.e., without interruption):


Using atomic class

By changing the counter to an std::atomic<int>, the increment operation (++counter) will be atomic, preventing race conditions.

Key features of std::atomic:

  • Atomic Operations: You can perform operations like increment, decrement, load, and store in a thread-safe way.
  • No Locks Required: Unlike "std::mutex, std::atomic" doesn't require explicit locks and has lower overhead since the operations are directly supported by the hardware.

In short, std::atomic is a class that helps manage shared data in a concurrent environment by ensuring that operations are atomic.

2. Using std::mutex

Another approach is to use a std::mutex to lock the shared resource while one thread is accessing it, ensuring that only one thread can modify the counter at a time:


Example with mutex instea do atomic

Explanation :

  1. Counter: The shared counter is now a regular int since we are using a mutex to control access to it.
  2. Mutex: The mutex named mtx is used to ensure that only one thread can increment the counter at any given time.
  3. Locking with lock_guard: Inside the worker() function, a lock_guard<mutex> object is created. This automatically locks the mutex and ensures it is unlocked when the object goes out of scope, ensuring exception safety and simpler code.
  4. Thread safety: The counter is protected from concurrent modifications by the two threads, eliminating race conditions.

In this version, the "lock_guard" ensures that the mutex is locked while a thread is incrementing the counter, preventing other threads from modifying it until the current thread is finished. This eliminates the race condition but may slightly reduce performance due to locking overhead.


Conclusion

Concurrency in C++ is crucial for writing efficient, scalable programs that take full advantage of multicore processors. However, managing shared resources between threads can introduce complexities, such as race conditions. Understanding and addressing these issues using tools like std::atomic and std::mutex ensures thread-safe operations and helps avoid subtle bugs.

By mastering these concepts, you can write high-performance, concurrent applications that scale effectively in real-world scenarios.


#Concurrency #C++ #Multithreading #Mutex #ProgrammingTips #SoftwareDevelopment #Risk

要查看或添加评论,请登录

Surya Ambati的更多文章

社区洞察

其他会员也浏览了