Nach C++11 gibt es eine Standard-Thread-Bibliothek: std::thread.

Früher verwendeten einige Compiler das Kompilierungsflag -std=c++11 für C++11

g++ -std=c++11 test.cpp 

std::thread-Konstruktor

Standardkonstruktor thread() noexcept;
Initialisierungskonstruktor template <class Fn, class... Args>
explicit thread(Fn&& fn, Args&&... args);
Kopierkonstruktor [gelöscht] thread(const thread&) = delete;
Verschiebekonstruktor thread(thread&& x) noexcept;
  • Standardkonstruktor, erzeugt ein leeresstd::threadAusführungsobjekt.
  • Initialisierungskonstruktor, erzeugt einstd::threadObjekt, dasstd::threadObjekt kann seinjoinable, wird der neu erzeugte Thread aufrufenfnFunktion, deren Parameter sindargsbereitgestellt von.
  • Kopierkonstruktor (gelöscht), was bedeutet, dassstd::threadObjekte können nicht kopierkonstruiert werden.
  • Move-Konstruktor. Move-Konstruktor (Move-Semantik ist ein neues Konzept in C++11, siehe Anhang für Details), nach einem erfolgreichen Aufrufxrepräsentiert keinenstd::threadAusführungsobjekt.

Hinweis: kann seinjoinablevonstd::threadObjekte müssen vom Hauptthread gejoint werden, bevor sie zerstört werdenjoinoder setzen Sie sie aufdetached.

std::threadBeispiele für verschiedene Konstruktoren sind wie folgt:

#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-Zuweisungsoperation

Move-Zuweisungsoperation thread& operator=(thread&& rhs) noexcept;
Kopierzuweisungsoperation [gelöscht] thread& operator=(const thread&) = delete;
  • Move-Zuweisungsoperation (1), wenn das aktuelle Objekt nichtjoinable, muss eine Rvalue-Referenz übergeben werden (rhs) anmovedie Zuweisungsoperation; wenn das aktuelle Objekt sein kannjoinable, dannterminate() wird aufgerufen, wodurch ein Fehler gemeldet wird.
  • Kopierzuweisungsoperation (2), gelöscht, daherstd::threadObjekte können nicht per Kopierzuweisung zugewiesen werden.

Siehe das Beispiel unten:

#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;
}

Weitere Memberfunktionen

get_id: Ruft die Thread-ID ab, gibt ein Objekt vom Typ std::thread::id zurück. Siehe das Beispiel unten:

#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: Prüft, ob der Thread joinable ist. Prüft, ob das aktuelle Thread-Objekt einen aktiven Ausführungsthread repräsentiert. Ein vom Standardkonstruktor erzeugter Thread kann nicht gejoint werden. Außerdem wird ein Thread, der seine Aufgabe beendet hat, aber nicht gejoint wurde, weiterhin als aktiver Ausführungsthread betrachtet und kann daher gejoint werden.

#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: Trennt den Thread ab. Trennt die vom aktuellen Thread-Objekt repräsentierte Ausführungsinstanz vom Thread-Objekt, sodass der Thread unabhängig ausgeführt werden kann. Sobald der Thread die Ausführung abgeschlossen hat, werden die ihm zugewiesenen Ressourcen freigegeben.

Nach dem Aufruf der detach-Funktion:

  • *thises repräsentiert keine Thread-Ausführungsinstanz mehr.
  • joinable() == false
  • get_id() == std::thread::id()

Darüber hinaus wird eine std::system_error geworfen, wenn ein Fehler auftritt oder joinable() == false ist.

#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: Tauscht Threads aus, tauscht die zugrunde liegenden Handles aus, die von den beiden Thread-Objekten repräsentiert werden.

#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();
}

Das Ausführungsergebnis ist wie folgt:

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: Gibt das native Handle zurück (da die Implementierung von std::thread mit dem Betriebssystem zusammenhängt, gibt diese Funktion das Thread-Handle zurück, das mit der spezifischen Implementierung von std::thread verbunden ist; zum Beispiel ist es auf POSIX-konformen Plattformen (wie Unix/Linux) die Pthread-Bibliothek).

#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();
}

Das Ausführungsergebnis ist wie folgt:

Thread 2 wird mit Priorität 0 ausgeführt Thread 1 wird mit Priorität 20 ausgeführt

hardware_concurrency [static]: Erkennt Hardware-Nebenläufigkeitsmerkmale, gibt die Anzahl der von der Thread-Implementierung auf der aktuellen Plattform unterstützten Thread-Nebenläufigkeit zurück, aber der Rückgabewert ist nur ein Systemhinweis.

#include <iostream>
#include <thread>

int main() {
  unsigned int n = std::thread::hardware_concurrency();
  std::cout << n << " concurrent threads are supported.\n";
}

Einführung in zugehörige Hilfsfunktionen im Namensraum std::this_thread

get_id: Ruft die Thread-ID ab.

#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: Der aktuelle Thread gibt die Ausführung ab, und das Betriebssystem plant einen anderen Thread ein, der die Ausführung fortsetzt.

#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: Der Thread schläft bis zu einem angegebenen Zeitpunkt, danach wird der Thread wieder aufgeweckt.

template< class Clock, class Duration >
void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );

sleep_for: Der Thread schläft für eine angegebene Zeitspanne, danach wird der Thread wieder aufgeweckt. Aufgrund von Thread-Planung und anderen Gründen kann die tatsächliche Schlafzeit jedoch länger sein als die durch sleep_duration repräsentierte Zeitspanne.

#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";
}

Das Ausführungsergebnis ist wie folgt:

Hello waiter
Waited 2000 ms

Quelle: https://github.com/forhappy/Cplusplus-Concurrency-In-Practice/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md