Comparison of C++ and Posix Threads
Amit Nadiger
Polyglot(Rust??, Move, C++, C, Kotlin, Java) Blockchain, Polkadot, UTXO, Substrate, Sui, Aptos, Wasm, Proxy-wasm,AndroidTV, Dvb, STB, Linux, Cas, Engineering management.
As most of us know the thread library was introduced in the C++11 standard along with a number of other language features and standard library components that were designed to support multithreaded programming, including atomic operations, mutexes, and condition variables. Together, these additions to the C++ standard library made it easier for developers to write correct and efficient multithreaded code in C++. The introduction of the thread's library was a significant addition to the C++ standard library and was motivated by the increasing importance of multithreaded programming in modern software development. Although these features were released in 2011, still I feel there is less familiarity w.r.t these new features. I am trying to uncover some basic concepts like why we need new feature as language lib when we already have POSIX, limitations, advantage, limitations ,etc. in this article.
It is useful to compare C++ threads and POSIX threads to understand the differences in functionality and features they provide. This knowledge can help in choosing the appropriate threading mechanism based on the requirements of the application being developed.
As said earlier C++ threads are a part of the C++ standard library and provide an object-oriented interface for creating and managing threads. They offer several useful features, such as support for thread-local storage, atomic operations, and the ability to wait on multiple threads. However, they may not be available on all platforms, and their implementation may vary between compilers.
On the other hand, POSIX threads are a standardized thread API for Unix-like operating systems. They provide a rich set of functionality, such as support for thread-local storage, synchronization primitives, and signal handling. They are widely available on most Unix-like systems, including Linux, macOS, and FreeBSD. However, they are not part of the C++ standard library and require more low-level programming compared to C++ threads.
By comparing C++ threads and POSIX threads, developers can understand the trade-offs and choose the appropriate threading mechanism based on the platform and requirements of their application.
Motivation for adding threads and other components as lang feature
Before the introduction of the threads library in C++11, C++ programmers had to rely on third-party libraries or platform-specific APIs to implement multithreading. The addition of the threads library to the C++ standard library provided a portable and standard way to implement multithreaded programming in C++, which made it easier for developers to write multithreaded code that could run on different platforms.
In addition to the threads library, C++11 also introduced a number of other language features and standard library components that were designed to support multithreaded programming, including atomic operations, mutexes, condition variables, and futures. Together, these additions to the C++ standard library made it easier for developers to write correct and efficient multithreaded code in C++.
How to create the thread using POSIX?
#include<pthread.h>
void *myThreadFunction(void *arg);
int main() {
pthread_t myThread;
pthread_create(&myThread, NULL, myThreadFunction, NULL);
// ...
pthread_exit(NULL);
}
void *myThreadFunction(void *arg) {
// thread code here
return NULL;
}
How to create a thread in C++?
There are several ways to create threads in C++ using the standard library.
Let me not go into detail about various ways to create threads using C++ std lib , I will try to write a separate article on this topic. But let me list down few of them here.
Example below:
#include<iostream>
#include <thread>
void my_thread_function(int* pData) {
std::cout << "Hello from my_thread_function!
and data passd ="<<*pData << std::endl;
}
int main() {
int data = 101;
std::thread my_thread(my_thread_function,&data);
my_thread.join();
return 0;
}
2. Using Functor:
3. Using a member function and an object:
4. Using a lambda function:
5. Using a packaged task
6. Using an async function:
7. Using the function std::jthread constructor: Introduced from C++20.
How are C++ threads implemented internally?
It's very important to know how C++ std implements the threads internally to compare with POSIX threads and also to decide which thread to use .
C++ uses the native OS threads utilities for implementing the threads, mutexes, and other synchronization primitives provided by the standard thread library.
When you create a thread using the std::thread class in C++, the implementation will use the underlying threading API provided by the operating system on which your program is running. For example, on Windows, the implementation will use the Windows threading API(CreateThread()), while on Linux, it will use the POSIX threading API (pthread_create()).
Similarly, mutexes, condition variables, and other synchronization primitives provided by the standard thread library are also implemented using platform-specific APIs, such as Windows mutexes and POSIX semaphores.
Using the native OS threads utilities provides several benefits, such as better performance, improved scalability, and more efficient use of system resources. Additionally, it also ensures that the behavior of the C++ standard library is consistent with the behavior of the underlying operating system, which makes it easier to write portable and reliable code.
When C++ std threads are created, they can be implemented using a combination of user-level and kernel-level threads. The exact implementation details may vary depending on the specific operating system and C++ standard library implementation.
At a high level, the C++ standard library implementation may use user-level threads for some operations, such as thread creation, scheduling, and synchronization. User-level threads are typically faster and more lightweight than kernel-level threads, as they can be managed entirely in user space without requiring any kernel involvement.
However, in some cases, the C++ standard library implementation may need to use kernel-level threads for certain operations, such as blocking I/O operations or system calls. Kernel-level threads are managed by the operating system's kernel and can take full advantage of multiple processor cores, making them more efficient for certain types of operations.
In order to achieve a balance between performance and efficiency, the C++ standard library implementation may use a combination of user-level and kernel-level threads, depending on the specific operation being performed.
For example, the implementation may use user-level threads for lightweight operations such as mutex locking and unlocking, while using kernel-level threads for heavier operations such as I/O operations or waiting on condition variables.
The combination of user-level and kernel-level threads allows the C++ standard library implementation to provide a fast and efficient threading model that can take advantage of multiple processor cores and provide high scalability. However, the exact implementation details may vary depending on the specific platform and library implementation of the C++ standard library that is used by the program. Different C++ standard libraries may use different threading models. For example, libstdc++ on Linux typically uses pthreads for implementing threads, which are kernel-level threads, while on some platforms, such as macOS and iOS, libc++ uses user-level threads provided by the operating system.
How to check which lib C++ threads use?
Run the ldd command that shows the shared libraries that are linked to the executable file and their memory addresses.
ex a.out.
Output put in my case :
It is better to refer to documentation for the C++ standard library and the documentation for the underlying operating system to understand the implementation details of the std::thread class and the threading API, respectively. This would help us to understand how the standard library is implemented and how it interacts with the underlying operating system
What is the difference between using the C++ std threads and POSIX threads?
This is the most important topic for application programmers.
How to decide which thread to use "C++ std thread or POSIX thread" ?
The decision of whether to use C++ std threads or POSIX threads (pthreads) depends on several factors, including the level of abstraction needed, the portability requirements of the code, and the familiarity of the programmer with the different thread models.
Here are a few factors to consider when deciding which thread model to use:
11. Project requirements: Finally, the specific requirements of the project may also influence the choice of threading model. For example, if the application needs to be highly scalable and performant, the developer may choose a threading model that is optimized for these requirements.
What are Limitation of C++ threads?
What are the limitations of POSIX threads:
4. No support for move semantics: POSIX threads do not support move semantics, which is a C++ feature that allows objects to be moved instead of copied. Move semantics can be more efficient than copying and can help avoid unnecessary memory allocations.
5. No support for async/await: POSIX threads do not have built-in support for async/await, which is a C++ feature that allows for more efficient asynchronous programming. Async/await can make it easier to write code that is both asynchronous and easy to read and understand.
Is it OK to use both C++std and POSIX threads in the same project?
From the possibility point of view Yes, it is possible to use both C++ threads and POSIX threads in the same project. However, there are some implications, problems, and concerns that developers should be aware of:
Let me illustrate more on this with an example.
When developing a software system, multiple libraries or components may be used in the project. These libraries can be developed by different teams, using different programming languages and platforms, and with different threading models. It's important to check the compatibility of all these components before integrating them into the final product.
For example, let's say you are developing a system in C++ that uses the Boost library for some functionality. Boost uses the C++11 threading model, which includes the std::thread class for creating and managing threads. However, you also want to use a third-party library that uses the POSIX threading model, which includes the pthread_create function for creating threads.
If you mix these threading models in your application, you may run into compatibility issues. For example, if you create a std::thread in your C++ code and pass it to the POSIX library, the POSIX library may not be able to correctly interact with the thread. Similarly, if you create a POSIX thread in the third-party library and try to pass it to the Boost library, the Boost library may not be able to interact with it correctly also very difficult to debug, performance tuning and maintain ,etc.
To avoid these issues, it's important to ensure that all components in the system use the same threading model. If this is not possible, it may be necessary to write wrapper functions or classes to bridge the gap between the different threading models. This can be time-consuming and error-prone, so it's important to carefully consider the threading models used by all components before integrating them into the final system.
Overall, using two different threading models in the same project is possible, but it should be done carefully and with consideration for the potential implications and problems.
Conclusion: Tt is difficult to conclude which threading model is better, as both C++ threads and POSIX threads have their advantages and disadvantages, and the choice of which to use often depends on the specific requirements of the application. C++ threads provide a higher-level abstraction for multithreading, with more convenient syntax and better integration with the rest of the C++ standard library, while POSIX threads offer a more fine-grained level of control and more portability across different platforms. Ultimately, the choice between the two should be based on factors such as performance requirements, platform support, ease of use, and compatibility with other libraries and frameworks used in the application.
Thanks for reading till end .I hope you will find this article useful in some way !
If you have any questions or suggestions, please leave a comment in the comment section.
C++ Cuda and AI Developer
1 年good article to learn more information about thread
Embedded Vision developer & Teacher
1 年Thanks for writing this useful guide
Computer science graduate & software developer | Java, C/C++, TypeScript | MySQL, MongoDB
1 年Very clear and concise, thanks for writing.
Senior Software Developer
1 年To me, using POSIX threads in C++ looks like following some policy of deliberately creating non-portable code.
Equity Derivatives IT Quant, Fixed Income Benchmarking-Lead Analyst
1 年Good Article . Please correct Comparision spelling in header "Comparison"