After C++11, there is a standard thread library: std::thread.
Previously, some compilers used the compilation flag -std=c++11 for C++11
g++ -std=c++11 test.cpp
std::thread constructor
| Default constructor | thread() noexcept; |
|---|---|
| Initialization constructor |
template <class Fn, class... Args> explicit thread(Fn&& fn, Args&&... args); |
| Copy constructor [deleted] | thread(const thread&) = delete; |
| Move constructor | thread(thread&& x) noexcept; |
- Default constructor, creates an empty
std::threadexecution object. - Initialization constructor, creates a
std::threadobject, whichstd::threadobject can bejoinable, the newly created thread will callfnfunction, whose parameters areargsprovided by. - Copy constructor (deleted), meaning that
std::threadobjects cannot be copy-constructed. - Move constructor. Move constructor (move semantics is a new concept in C++11, see appendix for details), after a successful call
xdoes not represent anystd::threadexecution object.
Note: can be
joinableofstd::threadobjects must be joined by the main thread before they are destroyedjoinor set them todetached.
std::threadExamples of various constructors 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 operation
| 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, an rvalue reference must be passed (rhs) tomovethe assignment operation; if the current object can bejoinable, thenterminate() will be called, reporting an error. - Copy assignment operation (2), deleted, therefore
std::threadobjects cannot be copy-assigned.
See the example below:
#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 example below:
#include <iostream>
#include <thread>
#include <chrono>
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t1(foo);
std::thread::id t1_id = t1.get_id();
std::thread t2(foo);
std::thread::id t2_id = t2.get_id();
std::cout << "t1's id: " << t1_id << '\n';
std::cout << "t2's id: " << t2_id << '\n';
t1.join();
t2.join();
}
joinable: Check whether the thread is joinable. Check whether the current thread object represents an active execution thread. A thread created by the default constructor cannot be joined. In addition, if a thread has finished its task but has not been joined, it will still be considered an active execution thread and therefore can be joined.
#include <iostream>
#include <thread>
#include <chrono>
void foo()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::thread t;
std::cout << "before starting, joinable: " << t.joinable() << '\n';
t = std::thread(foo);
std::cout << "after starting, joinable: " << t.joinable() << '\n';
t.join();
}
join: Join 线程,调用该函数会阻塞当前线程,直到由 *this 所标示的线程执行完毕 join 才返回。
#include <iostream>
#include <thread>
#include <chrono>
void foo()
{
// simulate expensive operation
std::this_thread::sleep_for(std::chrono::seconds(1));
}
void bar()
{
// simulate expensive operation
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::cout << "starting first helper...\n";
std::thread helper1(foo);
std::cout << "starting second helper...\n";
std::thread helper2(bar);
std::cout << "waiting for helpers to finish..." << std::endl;
helper1.join();
helper2.join();
std::cout << "done!\n";
}
detach: Detach the thread. Separate the execution instance represented by the current thread object from the thread object, so that the thread can execute independently. Once the thread completes execution, the resources allocated to it will be released.
After calling the detach function:
*thisit no longer represents any thread execution instance.- joinable() == false
- get_id() == std::thread::id()
In addition, if an error occurs or joinable() == false, a std::system_error will be thrown.
#include <iostream>
#include <chrono>
#include <thread>
void independentThread()
{
std::cout << "Starting concurrent thread.\n";
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Exiting concurrent thread.\n";
}
void threadCaller()
{
std::cout << "Starting thread caller.\n";
std::thread t(independentThread);
t.detach();
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Exiting thread caller.\n";
}
int main()
{
threadCaller();
std::this_thread::sleep_for(std::chrono::seconds(5));
}
swap: Swap threads, exchanges the underlying handles represented by the 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();
}
The execution result is 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: Return the native handle (since the implementation of std::thread is related to the operating system, this function returns the thread handle associated with the specific implementation of std::thread; for example, on 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();
}
The execution result is as follows:
Thread 2 is executing at priority 0 Thread 1 is executing at priority 20
hardware_concurrency [static]: Detect hardware concurrency features, returns the number of thread concurrency supported by the thread implementation on 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: Get 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 yields execution, and the operating system schedules another thread to continue executing.
#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 = start + us;
do {
std::this_thread::yield();
} while (std::chrono::high_resolution_clock::now() < end);
}
int main()
{
auto start = std::chrono::high_resolution_clock::now();
little_sleep(std::chrono::microseconds(100));
auto elapsed = std::chrono::high_resolution_clock::now() - start;
std::cout << "waited for "
<< std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count()
<< " microseconds\n";
}
sleep_until: The thread sleeps until a specified time point, then the thread is woken up again.
template< class Clock, class Duration > void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );
sleep_for: The thread sleeps for a specified time span, then the thread is woken up again. However, due to thread scheduling and other reasons, the actual sleep time may be longer than the time span represented by sleep_duration.
#include <iostream>
#include <chrono>
#include <thread>
int main()
{
std::cout << "Hello waiter" << std::endl;
std::chrono::milliseconds dura( 2000 );
std::this_thread::sleep_for( dura );
std::cout << "Waited 2000 ms\n";
}
The execution result is as follows:
Hello waiter Waited 2000 ms
Source: https://github.com/forhappy/Cplusplus-Concurrency-In-Practice/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md