Introduction
Multi-threading in applications are generally a headache to implement and debug. There are very specific windows API functions that have to be called in a specific order, and also we have to handle the locking and unlock of a critical section ourselves, which honestly is a pain. Due to the nature of writing codes that multi-threads, there are a lot of variables up in the air that the programmer has to manage. This then often introduces lots of unsightly code (mostly due to outdated Windows' API functions) and hard-to-solve bugs.
In C++11, the standard library has been expanded to introduce several facilities to deal with multi-threading applications.
To start threading
Firstly, there is std::thread. This standard class seeks to replace Window's CreateThread and that family of functions. When using the Windows API, the code for creating a new thread is as follows:
Multi-threading in applications are generally a headache to implement and debug. There are very specific windows API functions that have to be called in a specific order, and also we have to handle the locking and unlock of a critical section ourselves, which honestly is a pain. Due to the nature of writing codes that multi-threads, there are a lot of variables up in the air that the programmer has to manage. This then often introduces lots of unsightly code (mostly due to outdated Windows' API functions) and hard-to-solve bugs.
In C++11, the standard library has been expanded to introduce several facilities to deal with multi-threading applications.
To start threading
Firstly, there is std::thread. This standard class seeks to replace Window's CreateThread and that family of functions. When using the Windows API, the code for creating a new thread is as follows:
HANDLE myThread = CreateThread(NULL, 0, ThreadFunction, pArgument[0], 0, &threadID);
In order to call that function, we have to specify the default security attribute, then default stack size, the thread function to call, the argument to the thread function, the default creation flags, and a variable to get the thread identifier. That's a lot of work to create a thread.
std::thread on the other hand:
std::thread on the other hand:
std::thread myThread (ThreadFunction, Argument1, Argument2);
We only have to create the thread object and specify the thread function we want the thread to execute and also the arguments for the function, if it has any.
In those two examples, it is quite clear that the new C++11's implementation of threading functions is a lot more convenient to use than the window's functions.
There are several other useful features for multi-threading in the new C++11 as well. Below are just a few.
Critical Sections
Normally when we multi-thread, we need to be extremely careful about which thread is executing which piece of code at any one time in order to prevent multiple threads from reading and writing to the same variable at the same time. When using the Windows API, we have to lock the variable we want to read/modify to a single thread, and then unlock it after we are done with it. These sections are also known as Critical Sections, sections where you deem that bad things will happen when multiple threads run that piece of code at the same time. This often means protecting variables that might be modified or read by multiple threads at the same time. Therefore we have to protect those critical sections, making sure that only one thread has access at any one time.
When using the Windows API:
In those two examples, it is quite clear that the new C++11's implementation of threading functions is a lot more convenient to use than the window's functions.
There are several other useful features for multi-threading in the new C++11 as well. Below are just a few.
Critical Sections
Normally when we multi-thread, we need to be extremely careful about which thread is executing which piece of code at any one time in order to prevent multiple threads from reading and writing to the same variable at the same time. When using the Windows API, we have to lock the variable we want to read/modify to a single thread, and then unlock it after we are done with it. These sections are also known as Critical Sections, sections where you deem that bad things will happen when multiple threads run that piece of code at the same time. This often means protecting variables that might be modified or read by multiple threads at the same time. Therefore we have to protect those critical sections, making sure that only one thread has access at any one time.
When using the Windows API:
// lock a critical section
// doing fancy stuff to make games work
// unlock a critical section
std::atomic
std::atomic is C++11's simplified answer to race conditions in the reading and writing of variables.
As the name suggests, std::atomic are objects that encapsulate a value who is guaranteed to not cause race conditions, where each operation is done to the std::atomic value is always resolved as an atomic action. Using this is really simple as well.
std::atomic is C++11's simplified answer to race conditions in the reading and writing of variables.
As the name suggests, std::atomic are objects that encapsulate a value who is guaranteed to not cause race conditions, where each operation is done to the std::atomic value is always resolved as an atomic action. Using this is really simple as well.
std::atomic start (true); // create the atomic variable
... // start of a function called by multiple threads
start = false;
... // end of a function called by multiple threads
The overloaded operator in the class does the heavy work of preventing race conditions for you, you just have to use it like all the other normal variables, instead of manually locking and unlocking a piece of code to protect the variables.
Conclusion
Overall, the new functions that c++11 gave us has a few advantages in portability of the application, since what it offers is the interface for us to implement our multi-threading application. With this, we can write codes that we can compile with different c++ compiler that supports c++11. Also, since these APIs are not tied to a specific platform like how Windows multi-threading functions are, the client's code will not have to change if the implementation of how the threads are handled internally is modified.
Conclusion
Overall, the new functions that c++11 gave us has a few advantages in portability of the application, since what it offers is the interface for us to implement our multi-threading application. With this, we can write codes that we can compile with different c++ compiler that supports c++11. Also, since these APIs are not tied to a specific platform like how Windows multi-threading functions are, the client's code will not have to change if the implementation of how the threads are handled internally is modified.