C C++ Java Data Structure Python JavaScript CSS Ajax PL/SQL PHP Puzzles C programs C++ programs Java programs

Home » Expert's columns

Top 7 mistakes made while using C++ Multithreading

In this article, we are going to learn about top 7 mistakes, which we made while C++ programming’s Multithreading feature.
Submitted by Saurabh Hooda, on February 13, 2018

7 mistakes made while using C++ Multithreading

C++ is one of the most powerful object-oriented languages there is, if not the strongest. It’s a multi-platform titan that allows total flexibility, fast and robust development. It’s also well suited for heavy applications, like game engines and some AI applications, to mention at the very least. One of the serious benefits of writing in c++ is the resource efficiency of its code. Because of that, C++ performance almost outwits other programming languages, sometimes by a lot.

Since resources are really important to us, and since we’re soon to hit the barriers of Moore’s law, we need to optimize for a multi-core handling, which is harder to do in older languages like C++ that was created in the time of single cores. However, with C++11, we now have a versatile library to do so, thanks to threads.

What is a thread? What is multithreading? Why is it important?

The formal definition of a thread is:

A thread of execution is the smallest sequence of programmed instructions that can be managed independently by a scheduler, which is typically a part of the operating system.

Ref: https://en.wikipedia.org/wiki/Thread_(computing)

In a nutshell, it’s an independent set of values for the registers inside a core; these values contain the instruction pointer and the stack pointer as well. This allows the thread to work in coherent order and maintain a unique area of the memory dedicated to the thread.

Each thread defines a certain path of execution.

Multithreading is a form of multi-tasking, or so to speak. It allows two pieces of the same program to run concurrently. This greatly increases the performance whilst maintaining an efficient resources management. Here’s a quick demo of a code I’ve written that we’ll be using throughout this post. Here I defined a function called first thread which prints numbers from 0 up to 1000 in an ascending order, in the main function I print the first 100 ASCII characters, when I detach the thread, the printing gets all mixed up because of the parallel execution.

program, output - 7 mistakes made while using C++ Multithreading

But we all know that great power comes with great responsibilities. So what are the most famous mistakes to watch out for in multithreading?

1) Not using std::join() to wait for the threads in the background before termination

Forgetting to either join the thread or make it unjoinable (detach) will cause a program failure. Why? Because when the program ends, the thread object goes out of scope. So the thread destructor is called, and the destructor itself checks first if the thread is joinable. If it’s joinable, std::terminate is called, but if you made the mistake and let it float like that, the program will crash, as shown in the picture.

Mistake 1) - 7 mistakes made while using C++ Multithreading

2) Joining a thread more than once

Likewise, you can’t join a thread more than once, even as a precaution. The program will crash because you’re sort of trying to join the execution lines of the thread to those of its originator. That’s one reason why should be focused when dealing with threads because this runtime error might very well be hard to spot in a huge pile of code.

3) Trying to join a detached thread

It’s kind of obvious that if you mark a certain thread as unjoinable then you shouldn't be able to join it. Indeed, it’s logical, but you’re prone to fall into this mistake because you might forget you did so in the first place. When you get into the zone, writing code, few hundreds of lines and you might forget what you did back there because you’re too focused on the present. That’s why despite the fact that this error seems quite silly, it’s very well known one.

4) Forgetting that a thread function’s arguments are passed by value by default

It’s a very common error, not just in multithreading, but almost in all domains of C++ programming to get confused between passing by value and passing by reference. For threads functions, the default is passing by value, which would result in unexpected behavior if treated as passing by reference. To pass the variables by reference, we need to specify that using the std::ref.

5) Unprotected shared resources/data

Remember that multithreading involves maximizing efficiency, which entails sharing resources and data across the threads. This could lead to an undefined or unpredicted behaviour that will mess the entire flow of the program. That’s why each thread should lock the resources it's using and unlock it after finishing preventing random access to these resources and manipulation while working.

If you have noticed in the first example, the printing was somewhat random, several ASCII characters would be printed successively, then some numbers would continue in no particular order or sequence. To control this, we use the std::mutex as shown in the image. This way, std::cout which is shared across the main thread and the mythread thread is being equally shared, each locking it for its own use then unlocking it for the other’s use, so the printed output corresponds to one number followed by one ASCII character.

Mistake 5) - 7 mistakes made while using C++ Multithreading

6) Forgetting to release the locks

What if you forgot to release the mutex lock? Well, other threads would be blocked from using the same resource which might leave the program hanging there. This is a reason why lock and unlock pair might not be the safest option either. A better option is to use std::lock_guard that attempts to take ownership of the mutex, and when it goes out of scope the lock-guard is destructed which releases the mutex.

7) Acquiring multiple locks in different order

Attempting to acquire the locks in the wrong order might cause a DEADLOCK. That is a thread is trying to gain access from a blocked thread. Which results in some kind of a loop where the program is just hanged there?

Final thoughts

There are a lot of famous mistakes out there to watch out for. The only way to reach full mastery of multithreading in C++ is through continuous practice and good foundations in that area. The topic of whether to use lock-free programming or not, it’s generally advised not to unless it’s necessary. For this purpose the in depth knowledge of C++ becomes essential. Seeing this vast demand of the language, the internet offers many C++ tutorials and courses over different levels in accordance to the expertise of the developer.



Liked this article? Do share with your friends :)


COMMENTS