Depuis C++11, il existe une bibliothèque de threads standard : std::thread.
Auparavant, certains compilateurs utilisaient l'option de compilation -std=c++11 pour C++11
g++ -std=c++11 test.cpp
Constructeur de std::thread
| Constructeur par défaut | thread() noexcept; |
|---|---|
| Constructeur d'initialisation |
template <class Fn, class... Args> explicit thread(Fn&& fn, Args&&... args); |
| Constructeur de copie [supprimé] | thread(const thread&) = delete; |
| Constructeur de déplacement | thread(thread&& x) noexcept; |
- Constructeur par défaut, crée un
std::threadobjet d'exécution vide. - Constructeur d'initialisation, crée un
std::threadobjet, quistd::threadobjet peut êtrejoinable, le thread nouvellement créé appellerafnfonction, dont les paramètres sontargsfournis par. - Constructeur de copie (supprimé), ce qui signifie que
std::threadobjets ne peuvent pas être construits par copie. - Constructeur de déplacement. Constructeur de déplacement (la sémantique de déplacement est un nouveau concept en C++11, voir annexe pour plus de détails), après un appel réussi
xne représente aucunstd::threadobjet d'exécution vide.
Remarque : peut être
joinabledestd::threadobjets doivent être rejoints par le thread principal avant d'être détruitsjoinou les définir àdetached.
std::threadVoici des exemples de divers constructeurs :
#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';
}
opération d'affectation de std::thread
| Opération d'affectation par déplacement | thread& operator=(thread&& rhs) noexcept; |
|---|---|
| Opération d'affectation par copie [supprimée] | thread& operator=(const thread&) = delete; |
- Opération d'affectation par déplacement (1), si l'objet courant n'est pas
joinable, une référence rvalue doit être passée (rhs) àmovel'opération d'affectation ; si l'objet courant peut êtrejoinable, puisterminate() sera appelée, signalant une erreur. - Opération d'affectation par copie (2), supprimée, par conséquent
std::threadles objets ne peuvent pas être affectés par copie.
Voir l'exemple ci-dessous :
#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;
}
Autres fonctions membres
get_id: Obtenir l'ID du thread, renvoie un objet de type std::thread::id. Voir l'exemple ci-dessous :
#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: Vérifier si le thread est joinable. Vérifier si l'objet thread actuel représente un thread d'exécution actif. Un thread créé par le constructeur par défaut ne peut pas être joint. De plus, si un thread a terminé sa tâche mais n'a pas été joint, il sera toujours considéré comme un thread d'exécution actif et pourra donc être joint.
#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: Détacher le thread. Séparer l'instance d'exécution représentée par l'objet thread actuel de l'objet thread, afin que le thread puisse s'exécuter indépendamment. Une fois que le thread a terminé son exécution, les ressources qui lui ont été allouées seront libérées.
Après avoir appelé la fonction detach :
*thisil ne représente plus aucune instance d'exécution de thread.- joinable() == false
- get_id() == std::thread::id()
De plus, si une erreur se produit ou si joinable() == false, une std::system_error sera levée.
#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: Échange les threads, échange les handles sous-jacents représentés par les deux objets thread.
#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();
}
Le résultat de l'exécution est le suivant :
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: Renvoie le handle natif (étant donné que l'implémentation de std::thread est liée au système d'exploitation, cette fonction renvoie le handle de thread associé à l'implémentation spécifique de std::thread ; par exemple, sur les plateformes conformes à Posix (comme Unix/Linux), il s'agit de la bibliothèque Pthread).
#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();
}
Le résultat de l'exécution est le suivant :
Le thread 2 s'exécute à la priorité 0 Le thread 1 s'exécute à la priorité 20
hardware_concurrency [static]: Détecte les fonctionnalités de concurrence matérielle, renvoie le nombre de threads concurrents pris en charge par l'implémentation des threads sur la plateforme actuelle, mais la valeur de retour n'est qu'une indication système.
#include <iostream>
#include <thread>
int main() {
unsigned int n = std::thread::hardware_concurrency();
std::cout << n << " concurrent threads are supported.\n";
}
Introduction aux fonctions utilitaires associées dans l'espace de noms std::this_thread
get_id: Obtenir l'ID du thread.
#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: Le thread actuel cède l'exécution, et le système d'exploitation planifie un autre thread pour continuer l'exécution.
#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: Le thread dort jusqu'à un point temporel spécifié, puis le thread est réveillé à nouveau.
template< class Clock, class Duration > void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );
sleep_for: Le thread dort pendant une durée spécifiée, puis le thread est réveillé à nouveau. Cependant, en raison de l'ordonnancement des threads et d'autres facteurs, la durée de sommeil réelle peut être plus longue que la durée représentée par 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";
}
Le résultat de l'exécution est le suivant :
Hello waiter Waited 2000 ms
Source : https://github.com/forhappy/Cplusplus-Concurrency-In-Practice/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md