C ++ 11 Multithread-Programmierung (Notizen)


Die C ++ 11-Standardbibliothek bietet die Klasse std :: tread für die Multithread-Programmierung.

Thread erstellen und blockieren

Drei Möglichkeiten zum Erstellen von Threads

  1. Funktionsname oder Funktionszeiger zum Erstellen eines Threads
  2. Funktionsobjekt (Funktor) erstellt Thread
  3. Lambda-Ausdrücke erstellen Threads.

Blockieren des Hauptthreads: Join () - und Detach () -Funktionen

Der Unterschied zwischen den beiden: Das untergeordnete Thread-Objekt ruft die join()Funktion auf, der Haupt-Thread wird blockiert, bis die Ausführung des untergeordneten Threads abgeschlossen ist und der Haupt-Thread weiter ausgeführt werden kann. Nachdem das untergeordnete Thread-Objekt die detach()Funktion aufgerufen hat, wird der ausgeführte untergeordnete Thread vom Thread-Objekt getrennt und blockiert nicht den Betrieb des Haupt-Threads.

Das Thread-Objekt der Thread-Klasse muss eine join()Funktion oder detach()Funktion aufrufen, bevor es zerstört wird . Zu diesem Zeitpunkt hat das Thread-Objekt keinen zugeordneten Thread und befindet sich in einem unjoinableZustand, und der Destruktor kann normal aufgerufen werden. Wenn dem aktuellen Thread-Objekt ein Thread zugeordnet ist, dh sich in einem joinableZustand befindet und der Destruktor des Objekts zu diesem Zeitpunkt aufgerufen wird, tritt eine abnormale Operation auf terminate called without an active exception.
PS: Objekte der Klasse std :: tread können nicht kopiert werden, sondern nur die Übertragung von Objektressourcen. Wenn std :: move () verwendet wird, um den Ausführungsthread vom aktuellen Objekt auf andere Objekte zu übertragen, befindet sich das aktuelle Objekt ebenfalls im unjoinableStatus.

#include<thread>
#include<iostream>

using namespace std;


void thread_function(int n) {
    
    
  for(int i = 0; i < 100; i++){
    
    
      cout << "thread function " << n << " excuting" << endl;
  }
}

void (*f)(int n); // 声明函数指针

class DisplayThread {
    
    
 public:
  void operator() (int n) {
    
    
    for (int i = 0; i < 100; i++){
    
    
      cout << "Display Thread " << n <<" Excecuting" << endl;
    }
  }
};

int main(){
    
    
    /* 线程的创建者(父线程)必须管理创建的线程(子线程),应该等到子线程完成其任务或者让子线程从自己身上脱离。*/
    // 线程创建
    f = thread_function;
    // thread threadObj(thread_function);  // 用函数名创建线程。函数名同数组名一样都是个常量,表示函数体的首地址,并不是完全意义上的函数指针。
    thread threadObj(f,1); //用函数指针创建线程
    
    DisplayThread objectFunc;
    // objectFunc(); //objectFunc()调用与thread_function()函数调用形式上完全一样
    thread threadObj2(objectFunc,2); // 函数对象创建线程(函数对象是类的实例,调用操作符()被重载)
    
    thread threadObj3([]{
    
      //lambda表达式创建线程
        for (int i = 0; i < 100; i++){
    
    
          cout << "thread lambda expression excuting" << endl;
        }   
    });
    threadObj3.join(); // block线程,直到线程执行完毕,线程对象可以被销毁(threadObj3执行完后,才会接下去执行)
    // threadObj3.detach();  // 将线程从线程对象中分离,线程对象可以被销毁(被分离的线程仍与其它的线程并行执行)
    for (int i = 0; i < 20; i++){
    
    
        cout << "Display from MainThread" << endl;
    }

    thread threadObj4; // 默认初始化对象,还未传入回调函数
    cout << "before starting, unjoinable: " << threadObj4.joinable() << '\n';//0
    threadObj4  = thread( (DisplayThread()) , 4);  // 函数对象创建线程(通过DisplayThread()创建DisplayThread类的临时对象)
    cout << "after starting, joinable: " << threadObj4.joinable() << '\n'; //1

    thread threadObj5 = std::move(threadObj4); // 线程对象不能拷贝,只能转移。move()将threadObj4对象的资源转移到threadObj5
    cout << threadObj.get_id() << endl;  // 2
    cout << threadObj2.get_id() << endl; // 3
    cout << threadObj3.get_id() << endl; // 4
    cout << "after move, joinable: " << threadObj4.joinable() << '\n'; // 0
    // cout << threadObj4.get_id() << endl;  // threadObj4对象的线程已经被转移,处于unjoinable状态
    cout << threadObj5.get_id() << endl; // 5
    cout << this_thread::get_id() << endl; //返回当前线程id, 1

    threadObj.join();   
    threadObj2.join();
    // threadObj4.join();  // unjoinable的线程对象不能调用join()或者detach()
    threadObj5.join();
    cout << "after join(), unjoinable: " << threadObj4.joinable() << '\n';//0
    cout << "Exit of Main function" << endl;
    return 0;
}

Zusammenfassung: Rufen Sie join () oder separate () nicht für Thread-Objekte auf, die nicht mit Ausführungsthreads verknüpft sind (nicht verbundener Status). Vergessen Sie nicht, join () oder separate () für Thread-Objekte aufzurufen, die Ausführungsthreads zugeordnet sind (verbindender Status).
PS: Wenn der Haupt-Thread ausgeht, werden diese abgelösten Threads ebenfalls angehalten und laufen nicht mehr, wenn der Haupt-Thread zerstört wird.

Gegenseitiger Ausschluss und Synchronisation von Threads

Mutex: Mutex-
Synchronisation: Mutex + Bedingungsvariable

Der Verwendungsprozess der Bedingungsvariablen (condition_variable):

  1. Der Thread, dem die Bedingungsvariable gehört, erhält den Mutex.
unique_lock<mutex> locker(mtx);
  1. Überprüfen Sie eine bestimmte Bedingung in einer Schleife. Wenn die Bedingung nicht erfüllt ist, blockieren Sie den aktuellen Thread, bis die Bedingung erfüllt ist. Wenn die Bedingung erfüllt ist, führen Sie sie nach unten aus.
while(!firstOK){
    
     cond.wait(locker); } 
// 当条件变量的对象调用wait()函数时,它使用互斥量锁住当前线程。
// 当前线程一直被阻塞,直到另外一个线程在同一个条件变量的对象上调用notify_one()或notify_all()函数来唤醒当前线程。
// 用while是因为如果当前条件不满足,线程就算被唤醒了,依然需要被继续阻塞等待条件满足。
  1. Nachdem ein Thread erfüllt die Bedingungen und führt, Anruf notify_one()oder notify_all()aufwachen eine oder alle wartenden Threads.
cond.notify_all();

Synchronisationsbeispiel: Leetcode-Druck in der richtigen Reihenfolge

#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<functional>
using namespace std;
class Foo {
    
    
public:
    int count = 0;
    Foo():firstOK(false), secondOK(false) {
    
    
        
    }

    void first(function<void()> printFirst) {
    
    
        unique_lock<mutex> locker(mtx);  // std::unique_lock<T>为锁管理模板类,std::unique_lock对象独占mutex对象的所有权,管理mutex对象的上锁和解锁操作
        // printFirst() outputs "first". Do not change or remove this line.
        printFirst();
        firstOK = true;
        count++;
        cond.notify_all();
    }

    void second(function<void()> printSecond) {
    
    
        unique_lock<mutex> locker(mtx);
        while(!firstOK){
    
    
            cond.wait(locker);
        }
        // printSecond() outputs "second". Do not change or remove this line.
        printSecond();
        secondOK = true;
        count++;
        cond.notify_all();
    }

    void third(function<void()> printThird) {
    
    
        unique_lock<mutex> locker(mtx);
        while(!secondOK){
    
    
            cond.wait(locker);
        }
        // printThird() outputs "third". Do not change or remove this line.
        printThird();
        count++;
        cond.notify_all();
    }
private:
    mutex mtx;
    condition_variable cond;
    bool firstOK;
    bool secondOK;
};



void printFirst(){
    
    
    cout << "first" << endl;
}

void printSecond(){
    
    
    cout << "second" << endl;
}

void printThird(){
    
    
    cout << "third" << endl;
}

int main(){
    
    
    // Foo* f = new Foo();
    // thread t1(&Foo::first, f, printFirst);
    // thread t3(&Foo::third, f, printThird);
    // thread t2(&Foo::second, f, printSecond);
    Foo f;
    // Foo* ff = new Foo();
    // std::thread类成员函数作为线程函数,第一个参数是函数指针,第二个参数是对象指针(在类成员函数中创建线程,即this指针)
    // 三个线程共用一个Foo的对象,共享互斥量、条件变量等
    thread t1(&Foo::first, &f, printFirst);
    thread t3(&Foo::third, &f, printThird);
    // thread t3(&Foo::third, ff, printThird); // 不是共用一个对象会出现死锁,因为两个对象之间的互斥量,条件变量不共享
    thread t2(&Foo::second, &f, printSecond);
    t1.join();
    t2.join();
    t3.join();
    cout << f.count << endl;
    // cout << ff->count << endl;
}

PS: std :: thread Klassenmitgliedsfunktion als Threadfunktion, der erste Parameter ist ein Funktionszeiger, der zweite Parameter ist ein Objektzeiger (erstellen Sie einen Thread in der Klassenmitgliedsfunktion, dh diesen Zeiger). Referenzblog

Ich denke du magst

Origin blog.csdn.net/XindaBlack/article/details/105525148
Empfohlen
Rangfolge