c++ マルチスレッド学習 11 packaged_task と async

1. packaged_task は関数のパッケージ化を非同期的に呼び出します

packaged_task クラスの主な
機能は, 関数をオブジェクトにパッケージ化することです. このオブジェクトにアクセスするとき, 関数呼び出しと戻り値の取得は2つのステップに分けられます. つまり, 通常の呼び出し可能な関数オブジェクトを非同期実行タスクに変換
ます. .

string TestPack(int index)
{
    
    
    cout << "begin Test Pack " << index << endl;
    this_thread::sleep_for(2s);
    return "Test Pack return";
}
int main(int argc, char* argv[])
{
    
    
    packaged_task< string(int) > task(TestPack);
    auto result = task.get_future();
    //task(100);
    thread th(move(task),101);
    
    cout << "begin result get" << endl;

    //测试是否超时
    for (int i = 0; i < 30; i++)
    {
    
    
        if (result.wait_for(100ms) != future_status::ready)
        {
    
    
            continue;
        }
    }
    if (result.wait_for(100ms) == future_status::timeout)
    {
    
    
        cout << "wait result timeout" << endl;
    }
    else
        cout << "result get " << result.get() << endl;
    th.join();
    getchar();
    return 0;
}
packaged_task< string(int) > task(TestPack);

< string(int) > は関数ポインタ型、string は戻り型、int はパラメータ型、TestPack は関数ポインタで、TestPack 関数はタスクごとにパッケージ化されています。

auto result = task.get_future();

std::packaged_task の get_future メンバーを呼び出して、共有状態を std::future オブジェクトの結果に関連付けます。呼び出し後、両方のオブジェクトは同じ共有状態を共有します: (1).std::packaged_task オブジェクトは非同期プロバイダー (非同期プロバイダー) であり、格納されたタスクを呼び出すことによって、共有状態をある時点で準備完了に設定する必要があります。(2). std::future オブジェクトは、共有状態の値を取得し、必要に応じて準備が整うまで待機できる非同期の戻りオブジェクトです。

共有状態の有効期間は、少なくともそれに関連付けられている最後のオブジェクトが解放または破棄されるまで続きます。

 thread th(move(task),101);

スレッドの開始: このように左辺値がスレッドに渡されると、タスクのコピーであるため、まず move を使用してタスクを右辺値に変換します. この時点で、以前の方法は使用できなくなります. 101 はタスクにバインドされた関数のパラメーター

result.wait_for(100ms) != future_status::ready

結果が返されるのを待ちます。wait_for はタイムアウト期間を設定できます。タスクがタイムアウト期間内に完了すると、std::future_status::ready ステータスが返されます。タスクがタイムアウト期間内に完了しない場合は、それが返されます。 std::future_status::timeout status を返します。
ここに画像の説明を挿入
子スレッドで最初に 4 秒遅延して結果を取得します。

this_thread::sleep_for(4s);

get_future() は 3.1 秒後に返されるため、get_future() は future_status::timeout を返し
ここに画像の説明を挿入
、2 秒に変更します. 2 秒後にサブスレッドは結果を取得します. このとき、get_future() はタスクは 3.1 秒でラップされました。関数ポインタは関数の戻り値を指しています: 111111111111111111:
ここに画像の説明を挿入

次に、async は非同期スレッドを作成します

async 非同期呼び出し関数は C++11 で導入され、非同期 (マルチスレッド) 実装の複雑なプロセスをカプセル化し、std::promise、std::packaged_task と std::thread をカプセル化し、基本的に std: を置き換えることができます。すべてのものをスレッド化します。asyncはfuture<>に計算結果を保存し、get()で各オブジェクトの最終結果を取得する

構文 フューチャー オブジェクトauto future = async(launch::deferred, TestAsync,100);
を返します。最初のパラメーターはスタートアップ メソッド、2 番目のパラメーターは関数ポインター、3 番目のパラメーターは関数ポインターが指す関数のパラメーターです。

string TestAsync(int index)
{
    
    
    cout << index<<" begin in TestAsync " << this_thread::get_id() << endl;
    this_thread::sleep_for(2s);
    return "TestAsync string return";
}
int main(int argc, char* argv[])
{
    
    
   //创建异步线程
    
    cout << "main thread id " << this_thread::get_id() << endl;
    cout << endl;
   
    cout << "=====不创建线程直接启动异步任务====" << endl;
    auto future = async(launch::deferred, TestAsync,100);
    this_thread::sleep_for(3s);
    cout << "begin future get " << endl;
    cout << "future.get() = " << future.get() << endl;
    cout << "end future get" << endl;

    cout << endl;
    //创建异步线程
    cout << "=====创建异步线程====" << endl;
    auto future2 = async(TestAsync, 101);
    this_thread::sleep_for(2s);
    cout << "begin future2 get " << endl;
    cout << "future2.get() = " << future2.get() << endl;
    cout << "end future2 get" << endl;


    getchar();
    return 0;
}

ここに画像の説明を挿入

3 つの非同期起動メソッド:
(1) std::launch::async
非同期動作を保証します。つまり、渡された関数は別のスレッドで実行されます。
最初に表示が TestAsync で開始され、次に表示が開始されて未来の get が表示され、スレッド番号が一致せず、非同期スレッドが TestAsync で開始を表示する前に 3 秒間遅延します。非同期タスクは非同期スレッドによって呼び出されるため、次の文はasync は非同期スレッドと同時に呼び出され、非同期スレッドが最初に呼び出されます.begin future get がわずか 2 秒の遅延で表示されるため、
ここに画像の説明を挿入
このメソッドは非同期スレッドを作成して非同期タスクを完了し、作成時に非同期タスクを呼び出します
(2) std::launch::deferred
非非同期動作、つまり、他のスレッドが将来にある場合 共有状態にアクセスするために get() が呼び出されると、関数が呼び出されます。
get() を呼び出す前に、関数ポインタが指す関数は呼び出されず、3 秒の遅延があり、begin future get が最初に表示され、次に get() が表示されることがわかります。関数ポインターが指す関数は、2 秒の遅延の後に表示されます。ということになるので、このメソッドはスレッドを作らずに非同期タスクを直接起動し、get() 非同期タスク(3) std::launch::async | std::launch::deferred の後にのみ呼び出しますこの起動戦略では、システムの負荷に応じて、非同期で実行される場合と実行されない場合があります。しかし、それを制御することはできません。

比較: https://zhuanlan.zhihu.com/p/547500742

おすすめ

転載: blog.csdn.net/qq_42567607/article/details/126088976