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 unstd::threadobjet d'exécution vide.
  • Constructeur d'initialisation, crée unstd::threadobjet, quistd::threadobjet peut êtrejoinable, le thread nouvellement créé appellerafnfonction, dont les paramètres sontargsfournis par.
  • Constructeur de copie (supprimé), ce qui signifie questd::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éussixne représente aucunstd::threadobjet d'exécution vide.

Remarque : peut êtrejoinabledestd::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 pasjoinable, 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équentstd::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