C++ std::thread
Category Programming Techniques
Since C++11, there has been a standard thread library: std::thread.
Previously, some compilers used the C++11 compilation parameter -std=c++11.
g++ -std=c++11 test.cpp
std::thread Constructors
Default Constructor | thread() noexcept; |
---|---|
Initializer Constructor | template <class Fn, class... Args> <br>explicit thread(Fn&& fn, Args&&... args); |
--- | --- |
Copy Constructor [deleted] | thread(const thread&) = delete; |
--- | --- |
Move Constructor | thread(thread&& x) noexcept; |
--- | --- |
The default constructor creates an empty
std::thread
execution object.The initializer constructor creates a
std::thread
object that can bejoinable
. The newly created thread will call thefn
function with parameters given byargs
.The copy constructor (disabled) means that
std::thread
objects cannot be copied.The move constructor, the move constructor (move semantics is a new concept introduced in C++11, see appendix for details), after a successful call,
x
no longer represents anystd::thread
execution object.
>
Note: A joinable
std::thread
object must be joined
by the main thread or set to detached
before they are destroyed.
std::thread various constructor examples are as follows:
#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>
void f1(int n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread " << n << " executing\n";
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void f2(int& n)
{
for (int i = 0; i < 5; ++i) {
std::cout << "Thread 2 executing\n";
++n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int main()
{
int n = 0;
std::thread t1; // t1 is not a thread
std::thread t2(f1, n + 1); // pass by value
std::thread t3(f2, std::ref(n)); // pass by reference
std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread
t2.join();
t4.join();
std::cout << "Final value of n is " << n << '\n';
}
std::thread Assignment Operations
Move Assignment Operation | thread& operator=(thread&& rhs) noexcept; |
---|---|
Copy Assignment Operation [deleted] | thread& operator=(const thread&) = delete; |
--- | --- |
Move assignment operation (1), if the current object is not
joinable
, a right-value reference (rhs
) needs to be passed to the move assignment operation; if the current object isjoinable
,terminate
() will be called to report an error.Copy assignment operation (2), disabled, so
std::thread
objects cannot be copied.
See the following example:
#include <stdio.h>
#include <stdlib.h>
#include <chrono> // std::chrono::seconds
#include <iostream> // std::cout
#include <thread> // std::thread, std::this_thread::sleep_for
void thread_task(int n) {
std::this_thread::sleep_for(std::chrono::seconds(n));
std::cout << "hello thread "
<< std::this_thread::get_id()
<< " paused " << n << " seconds" << std::endl;
}
int main(int argc, const char *argv[])
{
std::thread threads[5];
std::cout << "Spawning 5 threads...\n";
for (int i = 0; i < 5; i++) {
threads[i] = std::thread(thread_task, i + 1);
}
std::cout << "Done spawning threads! Now wait for them to join\n";
for (auto& t: threads) {
t.join();
}
std::cout << "All threads joined.\n";
return EXIT_SUCCESS;
}
Other Member Functions
get_id
: Get the thread ID, returns an object of type std::thread::id. See the following
Swap Thread: The Swap thread exchanges the underlying handles represented by two thread objects.
#include <iostream>
#include <thread>
#include <chrono>
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
void bar()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t1(foo);
std::thread t2(bar);
std::cout << "thread 1 id: " << t1.get_id() << std::endl;
std::cout << "thread 2 id: " << t2.get_id() << std::endl;
std::swap(t1, t2);
std::cout << "after std::swap(t1, t2):" << std::endl;
std::cout << "thread 1 id: " << t1.get_id() << std::endl;
std::cout << "thread 2 id: " << t2.get_id() << std::endl;
t1.swap(t2);
std::cout << "after t1.swap(t2):" << std::endl;
std::cout << "thread 1 id: " << t1.get_id() << std::endl;
std::cout << "thread 2 id: " << t2.get_id() << std::endl;
t1.join();
t2.join();
}
Execution results are as follows:
thread 1 id: 1892
thread 2 id: 2584
after std::swap(t1, t2):
thread 1 id: 2584
thread 2 id: 1892
after t1.swap(t2):
thread 1 id: 1892
thread 2 id: 2584
native_handle
: Returns the native handle (since the implementation of std::thread is related to the operating system, this function returns a thread handle related to the specific implementation of std::thread, for example, under Posix-compliant platforms such as Unix/Linux, it is the Pthread library).
#include <thread>
#include <iostream>
#include <chrono>
#include <cstring>
#include <pthread.h>
std::mutex iomutex;
void f(int num)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
sched_param sch;
int policy;
pthread_getschedparam(pthread_self(), &policy, &sch);
std::lock_guard<std::mutex> lk(iomutex);
std::cout << "Thread " << num << " is executing at priority "
<< sch.sched_priority << '\n';
}
int main()
{
std::thread t1(f, 1), t2(f, 2);
sched_param sch;
int policy;
pthread_getschedparam(t1.native_handle(), &policy, &sch);
sch.sched_priority = 20;
if(pthread_setschedparam(t1.native_handle(), SCHED_FIFO, &sch)) {
std::cout << "Failed to setschedparam: " << std::strerror(errno) << '\n';
}
t1.join();
t2.join();
}
Execution results are as follows:
Thread 2 is executing at priority 0 Thread 1 is executing at priority 20
hardware_concurrency [static]
: Detects hardware concurrency features and returns the number of threads supported by the thread implementation of the current platform, but the return value is only a system hint.
#include <iostream>
#include <thread>
int main() {
unsigned int n = std::thread::hardware_concurrency();
std::cout << n << " concurrent threads are supported.\n";
}
Introduction to related helper functions in the std::this_thread namespace
get_id
: Retrieves the thread ID.
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
std::mutex g_display_mutex;
void foo()
{
std::thread::id this_id = std::this_thread::get_id();
g_display_mutex.lock();
std::cout << "thread " << this_id << " sleeping...\n";
g_display_mutex.unlock();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t1(foo);
std::thread t2(foo);
t1.join();
t2.join();
}
yield
: The current thread gives up execution, and the operating system schedules another thread to continue execution.
```cpp
include <iostream>
include <chrono>
include <thread>
// "busy sleep" while suggesting that other threads run // for a small amount of time void little_sleep(std::chrono::microseconds us) { auto start = std::chrono::high_resolution_clock::now(); auto end