Thread creation using C++ std
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.
The threads library was introduced in the C++11 standard, which was released in 2011. The motivation to add threads in C++ was to provide a standardized, portable, and efficient way to write concurrent programs in C++. Prior to the C++11 standard, the C++ language did not provide a standard way to create and manage threads, and developers had to rely on platform-specific APIs such as POSIX threads or Windows threads. This made it difficult to write portable and maintainable concurrent code. With the addition of the thread library to the C++ standard, developers can now write portable and standardized multithreaded code that can run on any platform that supports the C++ standard library.
Additionally, the rise of multicore processors made multithreaded programming an important technique for improving the performance of software applications. The addition of the thread library to C++ provides developers with a powerful tool for writing efficient and scalable multithreaded code that can take advantage of the increased processing power of modern CPUs.
How to create thread in C++ std?
There are several ways to create threads in C++ using the standard library.
Let me list down few of them here:
1.Using the function std::thread constructor: This is the most common way to create threads in C++. You can pass a function or a lambda expression to the constructor, and a new thread will be created that will execute that function or lambda expression.
#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;
}
// O/P - Hello from my_thread_function! and data passd =101
2. Using Functor: In C++, a functor is an object that can be treated like a function or a function pointer. Functors are also known as function objects, as they encapsulate a function or a code block within an object, which can be called like a regular function.
Functors are often used in C++ to provide a more flexible and object-oriented way of calling functions. They can be used to provide additional state or context to a function, which can be helpful in some cases, such as when working with algorithms that require a comparison function or when implementing callbacks and of course in this case thread creation.
To create a functor in C++, you define a class with an overloaded operator() function. This allows instances of the class to be called like a regular function.
#include <iostream>
#include <thread>
class MyThread {
public:
? ? void operator()() {
? ? ? ? std::cout << "Hello from thread!\n";
? ? }
};
int main() {
? ? std::thread t{MyThread()};
? ? t.join();
? ? return 0;
}
// O/P => Hello from thread!
3. Using a lambda function: Allows you to define an anonymous function on the fly. It is a shorthand way of defining a function object (a functor) without having to define a separate class or function for it. Lambda functions can be used wherever a function object is required, such as in STL algorithms, for callbacks, threads or for creating closures. Lambda functions can capture variables from their enclosing scope by value or by reference and can have parameters and a return value. The syntax for defining a lambda function uses the lambda operator [], followed by a parameter list (if any), and then the function body as below:
[capture clause] (parameter list) -> return type { function body }
// Thread using lamda.
#include <iostream>
#include <thread>
int main() {
? ? std::thread t([](){
? ? ? ? std::cout << "Hello from lamda thread!\n";
? ? });
? ? t.join();
? ? return 0;
}
//Op => Hello from lamda thread!
4. Using a member function and an object: In the below example of creating a thread using a member function and an object, &my_object is passed as the second parameter to std::thread() because we need to provide the instance of the class on which the member function should be called.
When we pass a member function to the std::thread() constructor, we also need to provide the object on which that function should be called. The second parameter to the std::thread() constructor is a pointer to the object on which the member function should be called.
#include <iostream>
#include <thread>
class MyClass {
public:
? ? void my_member_function() {
? ? ? ? std::cout << "Hello from member function thread!\n";
? ? }
};
int main() {
? ? MyClass my_object;
? ? std::thread t(&MyClass::my_member_function, &my_object);
? ? //When the thread starts running, it will call my_member_function() on my_object.
? ? t.join();
? ? return 0;
}
// Op => Hello from member function thread!
5. Using a packaged task:A packaged_task is a class template in C++ that encapsulates a task or a callable object that can be invoked asynchronously. It provides a convenient way to package a function or callable object along with its associated arguments and return value.
The packaged task is a way to pass a function, which returns a value or takes an argument, to a thread for execution. It is useful when we want to execute a function in a thread and get its return value.
packaged_task can be used in scenarios where we want to perform some computation asynchronously and retrieve the result later. It can be used in combination with other concurrency-related classes such as thread, async, and future.
#include <iostream>
#include <future>
#include <thread>
using namespace std;
int add2Nums(int x, int y) {
? ? return x + y;
}
int main() {
? ? packaged_task<void()> task([](){
? ? ? ? cout << "Hello from packaged_taskthread 1!\n";
? ? });
? ? future<void> future = task.get_future();
? ? thread t(std::move(task));
? ? future.wait();
? ? if(t.joinable()) {
? ? ? ? t.join();
? ? }
? ??
// Second pkgd thread created with seperate function .
? ? packaged_task<int(int, int)> task1(add2Nums); // create packaged_task with my_function
? ? future<int> result = task1.get_future(); // get future from packaged_task
? ? thread t1(std::move(task1), 10, 20); // pass packaged_task to thread
? ? cout << "Waiting for result from secind thread ...\n";
? ? int res = result.get(); // wait for result from packaged_task
? ? cout << "Result from send packed thread : " << res << std::endl;
? ? if(t1.joinable()) {
? ? ? ? t1.join();
? ? }
? ? return 0;
}
// Op =>
//amit@DESKTOP-9LTOFUP:~/OmPracticeC++/Threads$ ./a.out
//Hello from packaged_taskthread 1!
//Waiting for result from secind thread ...
//Result from send packed thread : 30
6. Using an async function: Using the function async: This function creates a new thread and returns a future object that can be used to retrieve the result of the thread's execution. For example:
#include <iostream>
#include <future>
using namespace std;
int my_thread_function() {
? ? return 101;
}
int main() {
? ? future<int> result = async(launch::async, my_thread_function);
? ? cout << "From thread created using async Result: " << result.get() << std::endl;
? ? return 0;
}
// Op => From thread created using async Result: 101
7. Using the function thread constructor: This is a new feature introduced in C++20. It is similar to std::thread, but provides additional safety guarantees by automatically joining or detaching the thread when the std::jthread object is destroyed. This is not yet supported by the gcc compiler 12.2.0(The latest one when I was writing this article ). I could not test this , hence I am not writing the example code here.
领英推荐
When to use what ?
Above example use future object and async, below are details.
1.future : is a C++11 standard library class template that represents a value or an exception that may not be ready yet but will be available in the future. It provides a way to asynchronously retrieve the result of a function call.
Here's an example that shows how to use future
#include <iostream>
#include <future>
#include <thread>
// for using the ?std::this_thread::sleep_for
#include <chrono> //provides a set of classes to perform time-related operations.
using namespace std;
int add(int a, int b) {
this_thread::sleep_for(std::chrono::seconds(2));
return a + b;
}
int main() {
future<int> result = async(launch::async, add, 3, 4);
cout << "Waiting for result..." << std::endl;
int sum = result.get();
cout << "Result: " << sum << std::endl;
return 0;
}
// Op =>
// Waiting for result..
// Result: 7.
In the above example, async is used to asynchronously execute the add function with arguments 3 and 4. It returns a future<int> object that will eventually hold the result of the computation. The launch::async policy is used to ensure that the function runs asynchronously in a new thread.
The main thread then waits for the result using the get member function of the future object. The get function blocks until the result is available, at which point it returns the result.
Note that the add function deliberately sleeps for 2 seconds to simulate a long-running computation that might benefit from asynchronous execution.
2. async : Is a C++11 standard library function that creates and runs a function asynchronously, potentially on a different thread. It returns an object of type future, which can be used to retrieve the return value of the function.
The syntax for async is below :
future<T> async(launch policy, Callable&& f, Args&&... args);
where policy is an optional argument that specifies how the function should be executed, f is the function to execute, and args are the arguments to pass to the function.
The policy argument can take on one of three values:
Note: If we do not specify an launch policy. Its behavior will be similar to launch::async | launch::deferred.
Here's an example of how to use async to execute a function asynchronously:
#include <iostream>
#include <future>
int add(int x, int y) {
return x + y;
}
int main() {
// Create a future object by asynchronously executing the add function
std::future<int> fut = std::async(std::launch::async, add, 2, 3);
// Do other work while the function executes asynchronously
std::cout << "Doing other work..." << std::endl;
// Retrieve the result of the function
int result = fut.get();
// Print the result
std::cout << "The result is: " << result << std::endl;
return 0;
}
// Op => Doing other work..
// The result is: 5.
In this example, we create a future<int> object by calling async with add as the function to execute and 2 and 3 as the arguments to pass to the function. The async function returns immediately, and the function add is executed asynchronously in a separate thread.
While the function is executing asynchronously, the program can do other work. In this example, we print a message to the console. When the result of the function is needed, we call the get method on the future object to retrieve the result. Finally, we print the result to the console.
If you know any other way to create the thread using C++ std lib, please let me know in the comments.
Thank you for reading till end. I am planning to write the thread creation using POSIX in next article.