C++11 マルチスレッド プログラミング 1: マルチスレッドの概要

C++11 マルチスレッド プログラミング 1: マルチスレッドの概要

C++11 マルチスレッド プログラミング 2: マルチスレッド通信、スレッド同期、ロック

C++11 マルチスレッド プログラミング 3: ロック リソース管理と条件変数 

C/C++ の基礎、ブースト スレッドの作成、スレッドの同期 


0 背景

        マルチスレッドにより、プログラムの同時実行性を向上させることができます。同時実行性を向上させる方法は 2 つあります。1 つはマルチスレッドで、もう 1 つはマルチプロセスです。ただし、マルチスレッドの方がシステムの消費量が少なく、スレッドとスレッドの平均効率も向上します。プロセスはほぼ同じです。
プログラムを実行するとCPUリソースを占有しますが、CPUリソースには限りがあり、CPUが8個あれば100個のアプリケーションを実行する必要があるのに、なぜ同時に動作するのでしょうか?実際、これは幻想です。CPUは単位時間をいくつかの等しい部分に分割します。各部分は CPU のタイム スライスであり、ナノ秒またはマイクロ秒レベルです。タイム スライスが分割された後、システムはそれをスケジュールします。各スレッドが実行されます 毎回、CPU タイムスライスを確保する必要があります 確保した場合はスレッド実行されます 確保しなかった場合はレディ状態になります レディ状態のスレッドCPU 使用率関数を掌握していないため、CPU を使用する権利がありません。そうです。だから、掴み続ける必要があります。掴んだ後は、実行することができ、実行状態になります。タイム スライスが終了した、使い果たされると、準備完了状態に戻り、タイム スライスを取得し続けます。このサイクルが繰り返されます。この高速スイッチングにより、スレッドは実行中ですが、実際には停止したり実行したりしていますが、視覚の持続性により、この現象では、一時停止していることがわかりません。
 
        Linux カーネルにはプロセスのみがあり、スレッドはありません。スレッドは軽量プロセス (LWP: 軽量プロセス) です。Linux 環境でも、スレッドの本質はプロセスです。コンピュータ上で動作するプログラムは、一連の命令と命令パラメータの組み合わせであり、命令は確立されたロジックに従ってコンピュータの動作を制御します。オペレーティング システムはシステム リソースをプロセス単位で割り当てます。プロセスはリソース割り当ての最小単位であり、スレッドはオペレーティング システムによる実行のスケジューリングの最小単位であることがわかります

        複数のスレッドが同じアドレス空間を共有し、プロセスは独自の独立したアドレス空間を持ちます。スレッドはシステム リソースを節約し、効率を維持できるだけでなく、より高くすることができます。1
        つのアドレス空間内の複数のスレッドは排他的です。各スレッド独自のアドレス空間、独自のスタック領域、レジスタ(カーネルで管理)、スタック領域は4 つのメモリ領域のうち一時変数が格納される領域、レジスタは CPU 内のリソース、レジスタ リソースは CPU によって管理されます。カーネル領域のことなので、あまり詳しくは説明しません。複数のスレッドが
        アドレス空間を共有しますコード セグメント、ヒープ領域、グローバル データ領域、オープン ファイル (ファイル記述子テーブル) はすべてスレッドによって共有されます。

        オペレーティング システムがリソースを割り当てるときの最小単位は、仮想アドレス空間です。このアドレス空間は、は 1 つのプロセスによって使用され、各プロセスは仮想アドレス空間に対応します。1 つのプロセスは 1 つの CPU タイム スライスのみを取得できます(アドレス空間に 10 個のスレッドが存在する場合、10 個のスレッドすべてが CPU タイム スライスを取得できます)。スレッドが占めるシステム リソースは少なくなりますが、効率は低下しません)複数のスレッドを 1 つのアドレス空間に分割できます。有効なリソースに基づいて、より多くの CPU タイム スライスを取得できます。
 
        アドレス空間とは何ですか? ? アプリケーションを起動すると、下図のようなアドレス空間が得られますが、実際にはこれは 4 つのメモリ領域の拡張
        コード セグメントであり、下図の
        .text セグメントヒープ領域です。
        グローバルデータ領域は、new や malloc によって生成されたリソースが格納される場所であり、グローバル変数静的変数が格納される場所 (.bss セグメントまたは .data セグメント)です。

        このアドレス空間には、4 つのメモリ領域とその他のものが含まれています。上の図は、プロセス内のすべてのものを示しています。プロセスが開始されるたびに、そのようなアドレス空間が占有されます。別のプロセスを開始すると、次の結果が得られます。 2 つ目はこのような仮想アドレス空間です。複数のスレッドが同じアドレス空間を共有します。

        オペレーティング システムにスレッドが 1 つだけある場合、単一スレッドは単一プロセスと同じです。他のプログラムがそのスレッドをプリエンプトすることがないため、そのスレッドは常に実行されます。これが、上の図に示されている単一スレッドが示していることです。 、彼は処刑されました。複数のスレッドがある場合、CPU が複数のスレッドを処理している場合、CPU 時間はいくつかのフラグメントに分割され、各スレッドは 1 つだけを取得し、取得した後実行され、実行後は CPU の使用権を放棄します。マルチスレッド この図は理想的すぎます。システムのバックグラウンドでは、これらのプリエンプションはランダムです。スレッド 1 は最初の 3 回で CPU タイム スライスを取得した可能性があり、スレッド 2 は最初の 10 回で CPU タイム スライスを取得した可能性があり、スレッド3 人は 2 ~ 5 回つかんだかもしれません。それはすべて可能であり、実行ごとに異なります。マルチスレッドの実行は、無秩序な実行状態です。
 
CPU スケジューリングとスイッチング: スレッド コンテキストのスイッチングはプロセスよりも高速 マルチ
        コンテキスト スイッチング: プロセス/スレッドのタイムシェアリングにより CPU タイム スライスが多重化されます スイッチング前に、前のタスクのステータスが保存されます (レジスタに保存されます)。初めてこのタスクに戻る場合は、この状態をレジスタからロードして実行を継続します。タスクの保存から再度ロードするまでのプロセスはコンテキスト スイッチです。
スレッドは安価で、より速く起動し、すぐに終了し、システム リソースへの影響はほとんどありません。
        スレッドの終了は最大でも 1 つの仮想アドレス空間のみを破壊し、複数のプロセスの終了は複数の仮想アドレス空間を破壊するため、 を解放するとき、スレッドは解放されます。複数のプロセスを考慮する必要があるのはどのような
場合ですか?
        アプリケーションaがある場合、aを実行する際にプログラムbを起動する必要があります。つまり、別のプロセスを起動することになります。このとき、プロセスに基づいてこれを完了する必要があります。別のプロセスを起動するのではなく、タスクを実行するだけであれば、ディスク, アプリケーションはスレッドのみを考慮する必要があります。CPU に
 
        8 コアがある場合、同時に 8 つのタスクを処理できます。16 スレッドがある場合でも、CPU タイム スライスの時分割多重化を使用して処理されます。理想的には、各コアは 2 つのスレッドを処理し、これら 2 つのスレッドは CPU タイム スライスをタイムシェアする必要があります。160 スレッドの場合、各コアには 20 スレッドの負荷がかかります。20 スレッドは CPU タイム スライスをタイムシェアして多重化します。切り替えの切り替えに時間がかかり、時間が消費されます。マルチタスクを処理する場合はマルチスレッドを使用してください。複数のプロセスを使用するよりも有利ですが、スレッド数が多いほど必ずしも良いとは限りません。スレッド数を制御するにはどうすればよいですか? 複雑なアルゴリズム (主に CPU によって実行されるため、負荷が高い) を処理するには、スレッド数 = CPU のコア数 (最も効率的)
        ファイル IO 操作:ファイル IO は CPU 使用率が高くないため、 CPU タイム スライスは時間内に多重化可能、スレッド数 = 2 * CPU コア数 (最も効率的)、 IO 操作には計算は必要ありませんが、メモリとディスク間の対話が必要です。ボトルネックは CPU 内にありますが、メモリとディスクの間のボトルネックです。メモリは電子デバイスであり、ハードディスクは機械式ハードディスクである可能性があるため、データを操作する際の機械機器と電子機器の動作効率は大きく異なります。 100万倍の差。


1.1 最初のスレッドのコード例

例を作成する

        マルチスレッド プログラミングでは、各プログラムは少なくとも 1 つのスレッドを実行します。一般的な main 関数は、メイン スレッドへの入り口として機能します。ここで、プロセスにはメイン スレッドが含まれ、プロセスには複数の子スレッドが含まれるため、一般にメイン スレッドのうちそれらを (つまり、メイン関数内で) サブスレッドを開始します。
 
        ヘッダー ファイルをインクルードする必要があります: #include <thread>
        使用するクラス: thread th(ThreadMain); スレッドが待機関数を作成してブロックを開始します: th.join(); ブロック解除         は、
        子スレッドが終了した後に解除されます。
子スレッドが終了するのを待ちます。終了した場合、つまり th.join(); を削除すると、メインスレッドの実行終了後に th オブジェクトが破棄されますが、サブスレッドはまだ実行中です。エラーがポップアップ表示されます。

 
        サブスレッドの場合、コールバック関数は実行後に自動的に終了し、メインスレッドは内部で作成されたスレッド オブジェクトを同時に破棄します。つまり、サブスレッドが最初に終了してからスレッド オブジェクトを破棄する必要があります

#include <thread>
#include <iostream>
//Linux 中要链接动态库 -lpthread
using namespace std;
 
void ThreadMain(){
    cout << "Sub  thread ID " << this_thread::get_id() << endl; // get_id()获取当前线程ID
    for (int i = 0; i < 5; i++){
        cout << "sub thread " << i << endl;
        this_thread::sleep_for(std::chrono::milliseconds(100));//100ms
    }
    cout << "end sub thread " << endl;
}

int main(int argc, char* argv[]){

    int num = thread::hardware_concurrency();
    cout << "CPU number: " << num << endl;  // 因为我用是虚拟机只分配了6个核心,所以显示6

    cout << "main thread ID " << this_thread::get_id() << endl;

    thread th(ThreadMain);    //线程创建并启动
    cout << "wait sub thread exit... " << endl;
    th.join();                //阻塞等待子线程退出

    cout << "end main thread " << endl;
    return 0;
}
#编译
g++ -o main main3.c -lpthread

 結果:

        スレッドの終了: ここでサブスレッドを作成しました。終了する前に 10 秒間実行すると、問題が発生します。無限ループがあると、単一の CPU のリソースが枯渇し、一部のタスクが終了します。処理中は、計算タスクではなく、特定の結果を待っているだけです。このとき、子スレッドの CPU リソースを解放することを選択できます。ここで使用される方法の 1 つはスリープです。スリープとは、現在のスレッドの実行時間です。 CPU を解放します。

        上記の 7 行目と 23 行目では、2 つの印刷出力が同時に実行される可能性がありますが、これらはマルチコア同時実行であるため、画面上の最終応答により 2 行のテキストが重なる可能性があります。

join() 関数

        この機能は、メインスレッドがサブスレッドの完了を待機し、その後メインスレッドが実行を継続することです

        子スレッドは join() と detach() のいずれか 1 つだけを呼び出すことができ、呼び出すことができるのは 1 回だけですjoinable() を呼び出すと、join() または detach() を正常に呼び出すことができるかどうかを判断できます。

#include <thread>
#include <iostream>

using namespace std;

void test(){
    cout << "子线程开始执行!" << endl;
    //do something
    cout << "子线程执行完毕!" << endl;
}

int main(){
    cout << "主线程开始执行!" << endl;

    thread t(test);

    // boolalpha 头文件#include <iostream> 让输出流将bool解析成为true或者 false   noboolalpha反之
    cout << "join()是否可调:" << std::boolalpha << t.joinable() << endl;

    t.join();   //主线程等待子线程

    cout << "join()是否可调:" << std::noboolalpha << t.joinable() << endl;
    cout << "主线程执行完毕!" << endl;
    return 0;
}

デタッチ()関数

        図を見ると、fun と main が交差して出力されており、fun の内容が最初に出力されていないことがわかります。その後、detach の機能によってメインスレッドとサブスレッドが分離されます。メインスレッドは待機しなくなります。実行するサブスレッド、つまり 2 つのスレッドが同時に実行される場合、メインスレッドが終了するとプロセスも終了します。

        デタッチ中、サブスレッドはメインスレッドの制御から切り離され、サブスレッドは独立してバックグラウンドで実行され、Linuxのデーモンプロセスに相当し、ホストされます。ランタイムライブラリによって。メインスレッドが終了するとプロセスも終了し、その後プロセスのすべてのスレッドも終了し、分離された子スレッドのリソースはランタイム ライブラリによって再利用されます。

スレッド名前空間 std::this_thread

        プロセスには作成状態、準備完了状態、実行状態、ブロック状態(一時停止状態)、終了状態(終了状態)の合計 5 つの状態があり、このうち作成状態と終了状態は非常に短時間維持されます。主に準備完了状態、実行状態、一時停止状態がわかりますが、スレッド作成後の状態もこれら 5 つの状態にあります。

sleep_for()

        スレッドが一定期間、期間タイプでブロックされ、その後ブロックが解除されることを指定します。

        この関数を呼び出したスレッドは即座に実行状態からブロック状態に遷移し、一定時間その状態でスリープしますが、ブロック状態のスレッドはCPUリソースを放棄しているため、コードは実行されません。処理中はスレッドがスリープするため、CPU に負担がかかりません。プログラムがスリープすると、ブロック状態から再び準備完了状態に変わります準備完了状態のスレッドは、CPU タイム スライスを再び競合する必要があります。それを取得した後、実行状態になり、プログラムは下に向かって走り続けます。

this_thread::sleep_for(std::chrono::seconds(1));//1s秒
this_thread::sleep_for(std::chrono::milliseconds(1000));//1000ms毫秒
this_thread::sleep_for(std::chrono::microseconds(1000*1000));//1000*1000us微妙
this_thread::sleep_for(std::chrono::nanoseconds(1000*1000*1000));//1000*1000*1000ns纳秒

sleep_until()

        指定されたスレッドは、指定された時点 time_point タイプまでブロックされ、その後ブロックが解除されます。

        C++11 では、日付と時刻に関連するライブラリ chrono が提供されており、このライブラリには主に、時間間隔の継続時間、クロック、および時点の 3 種類のクラスが含まれています。つまり、これら 2 つの関数で使用されるパラメータは

収率()

        スレッドでこの関数を呼び出した後、実行状態のスレッドは取得した CPU タイム スライスを積極的に放棄し、最終的には準備完了状態になるため、他のスレッドが CPU タイム スライスを取得できる可能性が高くなります。この関数を使用する際に注意すべき点の 1 つは、スレッドは yield() を呼び出した後積極的に CPU リソースを放棄しますが、準備が整ったスレッドはすぐに次の CPU 競合ラウンドに参加することです。 CPU タイムスライスの状況

        std::this_thread::yield() の目的は、1 つのスレッドが CPU リソースを長時間占有し、マルチスレッド処理のパフォーマンスが低下するのを防ぐことです
        。現在取得している CPU リソースを積極的に放棄しますが、次のラウンドでも引き続き取得します


1.2 スレッドオブジェクトのライフサイクルとスレッドの待機と分離

 

         エラーの理由はいくつかありますが、1 つ目はメイン スレッドが終了するため、まずメイン スレッドが終了していないことを確認する必要があります。

 しかし、結果は依然として上記と同じです。

メインスレッドと子スレッドを同時に実行したいのですが、 th オブジェクトを維持したくないのですが (もちろん、このオブジェクトを維持する別の方法があります)、どうすればよいでしょうか?

#include <thread>
#include <iostream>
//Linux -lpthread
using namespace std;
bool is_exit = false;//通过这个标志位通知子线程退出
 
void ThreadMain()
{
    cout << "begin sub thread main " << this_thread::get_id() << endl;
    for (int i = 0; i < 10; i++)
    {
        if (is_exit) break;
        cout << "in thread " << i << endl;
        this_thread::sleep_for(chrono::seconds(1));//1000ms
    }
    cout << "end sub thread main " << this_thread::get_id() << endl;
}
int main()
{
    {
        //thread th(ThreadMain);
        //th.detach();
        //主子线程分离,子线程就变成了在后台运行的线程,与主线程无关
        //坑:主线程退出后 子线程不一定退出,那造成一个什么现象,主线程退出之后,主线程的全局空间,
        //栈空间等全部都释放掉了,一旦子线程访问了这些空间,那么程序就会崩溃掉,在Windows中,写完程序
        //关闭的时候弹出一个错误窗口,那么多半是你还在运行的线程访问了静态成员变量或者全局变量,因为
        //变量都被销毁掉了,你还在访问它,那么就会出现错误。
    }
    {
        thread th(ThreadMain);
        this_thread::sleep_for(chrono::seconds(1));//1000ms
        is_exit = true; //通知子线程退出
        cout << "主线程阻塞,等待子线程退出" << endl;
        th.join(); //主线程阻塞,等待子线程退出
        cout << "子线程已经退出!" << endl;
    }
    //getchar();
    return 0;
}

 

        では、この状況を回避するにはどうすればよいでしょうか? detach() 関数を使用してサブスレッドを切り離し、最終的にプログラムがクラッシュした場合、サブスレッドは外部変数にアクセスせず、スレッド関数内の変数にのみアクセスするか、メインスレッドの終了後に終了する必要があります。ただし、この方法は実際の問題が多く、デタッチが行われない場合が多く、 th のようなオブジェクトを維持する必要があり、そうでない場合はオブジェクトを削除することになります。子スレッドがまだ実行されている間にエラーが報告されるため、プログラム全体が破棄されたときに join 関数を呼び出すだけで済みます。


1.3 グローバル関数はスレッドエントリ分析パラメータとしてメモリを渡します

#include <thread>
#include <iostream>
#include <string>
//Linux -lpthread
using namespace std;

class Para{
public:
    Para() { cout << "Create Para" << endl; }
    Para(const Para& p)
    {
        cout << "Copy Para" << endl;
        this->name = p.name;
    }
    ~Para() { cout << "Drop Para" << endl; }
    string name;
};
 
void ThreadMain(int p1, float p2, string str, Para p4){
    //延时100ms,确保f1被释放掉,因为此时主线程已经走到th.join();了
    //f1传过来后是拷贝,因此外部f1的销毁这里不受影响
    this_thread::sleep_for(100ms);
    cout << "ThreadMain " << p1 << " " << p2 << " " << str << " " << p4.name << endl;
}
 
int main(int argc, char* argv[]){
    thread th;
    //在栈空间中,一对大括号之后就会释放
    {
        //当这个f1释放之后,当调用thread创建线程这一行代码的时候,后面的参数都做了一份拷贝
        float f1 = 12.1f;
        Para p;
        p.name = "test Para class";
        //所有的参数做复制
        th = thread(ThreadMain, 101, f1, "test string para", p);
    }
    th.join();
    return 0;
}
#编译
g++ -o main main3.c -lpthread

これは Linux での出力です

 

        これは、Windows での VSstudio の出力です。なぜ 2 つの結果が異なるのか、少し混乱しています。Windows の結果の説明は次のとおりです: 中かっこの真ん中に一度作成し、コピーのためにスレッド関数に渡します。一度構築し、コールバック関数が再度呼び出されたときに再度コピーして構築します。合計3回作成されました。したがって、値渡しの方法ではコピーのオーバーヘッドが大量に発生するため、参照渡しとポインター渡しの使用についてさらに考慮する必要があります。


1.4 スレッド関数はポインタと参照を渡します

パラメータの受け渡しには落とし穴がいくつかあります。受け渡しスペースが破壊されています。複数のスレッドがスペースへのアクセスを共有しています。渡されたポインタ変数のライフサイクルはスレッドよりも短いです。前のセクションのコードに基づいて、次のコードを追加します
。 :

アドレスの受け渡し:

このようなコードには正常にアクセスできます

これにより、通常のアクセスができなくなります。この問題に対処するには、オブジェクト p をヒープに直接配置するか、静的にするか、パラメータの宣言期間をスレッドのライフ サイクル内にする方法があります。 

参照渡し:

エラーの理由: スレッド関数の基になる実装は、テンプレート関数を使用して実装されています。テンプレートは特殊な関数を使用しているため、これが参照転送であることを認識できません。これが通常のパラメータであることしか認識できません。通常のパラメータを探しますが、対応する関数については、一致する関数が存在しないことが判明したため、ここでこれが参照転送であることを識別する必要があります。 

テンプレート関数を参照するときは、これを考慮する必要があります。テンプレートに参照が存在すると、それを参照型、つまり ref(p) としてマークする必要があるからです参照によっても、ポインタと同様のエラーが発生する可能性があります。

#include <thread>
#include <iostream>
#include <string>
//Linux -lpthread
using namespace std;
class Para
{
public:
    Para() { cout << "Create Para" << endl; }
    Para(const Para& p)
    {
        cout << "Copy Para" << endl;
        this->name = p.name;
    }
    ~Para() { cout << "Drop Para" << endl; }
    string name;
};
 
void ThreadMain(int p1, float p2, string str, Para p4)
{
    //延时100ms,确保f1被释放掉,因为此时主线程已经走到th.join();了
    this_thread::sleep_for(100ms);
    cout << "ThreadMain " << p1 << " " << p2 << " " << str << " " << p4.name << endl;
}
 
void ThreadMainPtr(Para* p)
{
    this_thread::sleep_for(100ms);
    cout << "ThreadMainPtr name = " << p->name << endl;
}
 
void ThreadMainRef(Para& p)
{
    this_thread::sleep_for(100ms);
    cout << "ThreadMainPtr name = " << p.name << endl;
}
int main(int argc, char* argv[])
{
/*
    {
        //传递引用
        Para p;
        p.name = "test ref";
        thread th(ThreadMainRef, ref(p));
        //thread th(ThreadMainRef, p);  这里会报错,原因:thread函数的底层实现是用模板函数实现的,模板用了一个特殊函数,导致他不能识别这个是引用传递,它只能识别到这是一个普通参数,所以他就会去找普通参数对应的函数,又发现没有与之匹配的函数存在,所以需要在这里标识一下这是一个引用传递。
        th.join();
    }
*/

/*
    {
        //传递线程指针
        Para p;
        p.name = "test ThreadMainPtr name";
        thread th(ThreadMainPtr, &p); //错误,线程访问的p空间会提前释放
        th.detach();
    }
    // Para类型的对象p放在栈空间,并且位于大括号内,所以会被释放
    getchar();
*/

/**/
    {
        //传递线程指针
        Para p;
        p.name = "test ThreadMainPtr name";
        thread th(ThreadMainPtr, &p);//可以正常访问
        th.join();
        getchar();
    }

    return 0;
}

 


1.5 メンバー関数はスレッドエントリとして機能します (オーバーライド、フラッシュ)

前のセクションではパラメータのライフサイクルについて説明しましたが、多くのパラメータを渡す必要がある場合はどうすればよいでしょうか? オブジェクトを使用してスレッドを作成することを検討する場合、オブジェクトのすべてのメンバーをパラメータとして使用できます。

#include <thread>
#include <iostream>
#include <string>
//Linux -lpthread
using namespace std;

class MyThread
{
public:
    //入口线程函数
    void Main()
    {
        cout << "MyThread Main " << name << ":" << age << endl;
    }
    string name;
    int age = 100;
};
 
int main(int argc, char* argv[])
{
 
    MyThread myth;
    myth.name = "Test name age";
    myth.age = 20;
    thread th(&MyThread::Main, &myth);
    th.join();
 
    return 0;
}

このようにして、メンバー関数をサブスレッドとして持つエントリ関数を作成します。
メンバー関数へのポインター: &MyThread::Main
メンバー関数には直接アクセスできません。オブジェクトのアドレスが必要です。つまり、this ポインターが必要です。現在のオブジェクトのアドレスは次のとおりです: &myth、&myth はこれと同等です

スレッド基本クラスの基本的な考え方:

#include <thread>
#include <iostream>
#include <string>
//Linux -lpthread
using namespace std;

class XThread
{
public:
    virtual void Start()
    {
        is_exit_ = false;
        th_ = std::thread(&XThread::Main, this);
    }
    virtual void Stop()
    {
        is_exit_ = true;
        Wait();
    }
    virtual void Wait()
    {
        if (th_.joinable())
            th_.join();
    }
    bool is_exit() { return is_exit_; }
private:
    virtual void Main() = 0;
    std::thread th_;
    bool is_exit_ = false;
};

class TestXThread :public XThread
{
public:
    void Main() override
    {
        cout << "TestXThread Main begin" << endl;
        while (!is_exit())
        {
            this_thread::sleep_for(100ms);
            cout << "." << flush;    // 屏幕输出的话是先输出到输出序列的缓冲区,不会立刻显示,所以要加一个flush进行刷新,确保立即显示。
        }
        cout << endl << "TestXThread Main end" << endl;
    }
    string name;
};

int main(int argc, char* argv[])
{
    TestXThread testth;
    testth.name = "TestXThread name ";
    testth.Start();
    this_thread::sleep_for(3s);
    testth.Stop();
    return 0;
}

  

 派生クラスで仮想関数をオーバーロードする場合は、キーワード override を追加して、コンパイラが正しくオーバーロードされているかどうかのチェックを支援できるようにします。このキーワードを追加しない場合、重大なエラーは発生しませんが、コンパイラはセキュリティに加えて、このキーワードは、作成した内容が間違っていないことを保証することを意味するため、コンパイル段階でエラーが報告されるまで待つ必要はありません。


1.6 スレッドエントリ関数としてのラムダ一時関数

通常の匿名関数は、スレッドのエントリ関数として機能します。

メンバー関数内の匿名関数は、スレッドのエントリ関数として機能します。 

#include <thread>
#include <iostream>
#include <string>
//Linux -lpthread
using namespace std;
// test lambda thread
class TestLambda
{
public:
    void Start()
    {
        thread th([this]() {cout << "name = " << name << endl; });
        th.join();
    }
 
    string name = "test lambda";
};
int main(int argc, char* argv[])
{
    thread th(
        [](int i) {cout << "test lmbda " << i << endl; },
        123
    );
    th.join();
    TestLambda test;
    test.Start();
 
    return 0;
}

おすすめ

転載: blog.csdn.net/qq_34761779/article/details/129209835