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 leeres
std::threadAusführungsobjekt. - Initialisierungskonstruktor, erzeugt ein
std::threadObjekt, dasstd::threadObjekt kann seinjoinable, wird der neu erzeugte Thread aufrufenfnFunktion, deren Parameter sindargsbereitgestellt von. - Kopierkonstruktor (gelöscht), was bedeutet, dass
std::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 Aufruf
xrepräsentiert keinenstd::threadAusführungsobjekt.
Hinweis: kann sein
joinablevonstd::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 nicht
joinable, 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, daher
std::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