C ++マルチスレッド非同期プログラミング---

 

メインスレッドの同期が(確立優先順位に従い、完了前の訪問を待ってから開始するのアクセスをブロックする必要があります)共有データの同期へのアクセスであるので、共有データへのアクセスの競合の問題を解決するために、メインスレッドの同期。非同期プログラミングについてのこの記事の会談は、主にタスクやスレッドの実行順序に対して向けられ、それが仕事であることは、実行が実行を開始する前に完了したタスクを待機してブロックする必要はありません、タスクと手順の順序の実行順序は、矛盾しています。解釈の角度からのタスクの同期および非同期実行順序とのここでの違い:

同期:結果を得ていない前に、コールを発行する際、呼び出しは戻らないだろうということ。しかし、呼び出しが戻った後、戻り値が得られます。言い換えれば、呼び出し側によって、このイニシアチブは、呼び出しの結果を待ちます。
非同期:問題後に呼び出されますが、コールは直接返されますので、結果は返されません。つまり、非同期プロシージャ・コールを発行した際、呼び出し側はすぐに結果を得ることはありません。しかし、コールが発行された後、状態、通知によって、発信者は、発信者に通知するために、またはコールバック関数を呼び出すことにより、これに対処します。


第二に、非同期プログラミングを使用する方法を
スレッドライブラリにはなりませんメソッドの実行スレッドを<スレッド>は、通常の場合は、スレッドの呼び出し側は、次のタスクを実行するためのスレッドの結果や実行状態を取得する必要があります。だから、何を意味するのかによって、結果や呼び出し先の状態を取得するには?

結果合格するグローバル変数と条件変数の2.1を使用
「予告-ウェイクアップ」と、前に述べた条件変数機能を、あなたは状態に結果を入れたり、グローバル変数を行うことができ、呼び出し側は、通知条件変数によって、タスクを実行しているとき発信者の結果やステータスが使用するように更新されました。プログラムの例を以下に示します:

// 呼び出し元のスレッドが戻る結果によって渡されたグローバル変数のfuture1.cpp使用、呼び出し元のスレッドに通知する条件変数を使用する結果となっている

の#include <ベクトル> 
の#include <数値> 
の#include <iostreamの> 
の#include <クロノ> 
書式#include <スレッド> 
#含める <ミューテックス> 
書式#include <condition_variable> int型 RES = 0 ;                         //は、グローバル変数の結果保存 
STDを:: MUミューテックス;                         // ミューテックスグローバル変数 
; STD :: condition_variable指揮        // グローバル状態変数が無効:累積(STDを:ベクトル< int型 > ::イテレータまず、
                STD ::ベクトル


 
< のInt > ::最終イテレータ)
{ 
    int型 SUM = STD ::累算(最初、最後、0);       // 標準ライブラリ加算機能 
    STDのunique_lockのは:: <STD ::ミューテックス>はロッカー(MU); 
    RES = SUM。
    locker.unlock(); 
    cond.notify_one();               // 通知待機スレッドに"条件が満たされた" 
} 
 
int型のmain()
{ 
    STD ::ベクトル < 整数 > = {数字123456 }。
    スレッドwork_thread :: STD(蓄積、numbers.begin()、numbers.end()); 

    STDのunique_lockのは:: <STD ::ミューテックス> ロッカー(MU); 
    cond.wait(ロッカー、[](){ 戻り RES。 });    // 条件変数がウェイクアップした場合、結果が変更されているかどうかをtrueに直接戻ることを確認し、falseを待ち続ける 
    のstd :: coutの<< 結果=  << RESを<< \ N- ; 
    ロッカー。 UNLOCK(); 
    work_thread.join();          // 実行完了するために、スレッドを待っている
 
    (getchar関数を); 
    の戻り 0 ; 
}

次のように関数が実行されます。
future1実行結果

結果は、非同期タスクの実行機能を得るために達成しているが、上記のコードから見て、より多くのグローバル変数を必要とすることができるように複雑なプログラムを書くときに、複数のスレッド間の結合度が高く、容易に導入バグです。非同期プログラミング、それを実装するための良い方法はありますか?C ++ 11には、<未来>のライブラリ関数は非同期プログラミングのための偉大な利便性を提供して追加します。

2.2および将来の約束の納入実績
特定の機能のための<将来>ヘッダ値プロバイダ非同期アクセスが提供されることができ、別のスレッドであってもよいです。
これらのプロバイダー(いずれかの約束のオブジェクト、packaged_taskオブジェクト、または非同期に非同期呼び出しのいずれか)と状態を共有するオブジェクトの将来の共有:共有状態プロバイダー準備アクセスポイントと目標点は、将来の状態の同期を共有しました。次のように<今後>ヘッダの構造は以下の通りであります:

値を返すか、例外が転送の交換に共有状態でスローされる複数のスレッド間で共有上記の状態を観察します。私たちは、呼び出し元のスレッドで共有ステータス戻り値または例外が書き込まれている変更することによって、呼び出し元のスレッドに通知することができ、共有状態を確保するために正しく配信スレッド間の戻り値または例外への鍵である場合には、共有データへの間でマルチスレッドの同時アクセスがペースを維持するために必要であることを知っています完全に、あなたがアクセスしたり操作することができます。将来の状態(future_status)は、次の三つがあります。

繰延:非同期操作はまだ始まっていない。
READY:非同期操作が完了しました;
タイムアウト:非同期操作がタイムアウトしました。
スレッドは、プロバイダおよび取得の共有状態を含む、戻り値または例外が共有状態を介している通るので、唯一のタスクまたはスレッド共有状態を含むオブジェクトを所有し、他のタスクまたはスレッドが共有状態を介して通知することができるように同期メカニズムは、人やスレッドの戻り値または例外を取得します。私たちは通常、共有状態を持っていないスレッドを作成<スレッド>を使用し、我々は、そのスレッドのための共有状態を提供する必要があるので、その戻り値またはアクセスの例外の後に。だから、それはスレッドで共有状態を含むオブジェクトをどのように提供しますか?これは、ヘルプのstd ::約束を必要とする<T>次のように使用されているクラステンプレートの実装、:

std::promise< T >构造时,产生一个未就绪的共享状态(包含存储的T值和是否就绪的状态)。可设置T值,并让状态变为ready。也可以通过产生一个future对象获取到已就绪的共享状态中的T值。继续使用上面的程序示例,改为使用promise传递结果,修改后的代码如下:

//future2.cpp 使用promise传递被调用线程返回结果,通过共享状态变化通知调用线程已获得结果

#include <vector>
#include <thread>
#include <future>
#include <numeric>
#include <iostream>
#include <chrono>
 
void accumulate(std::vector<int>::iterator first,
                std::vector<int>::iterator last,
                std::promise<int> accumulate_promise)
{
    int sum = std::accumulate(first, last, 0);
    accumulate_promise.set_value(sum);  // 将结果存入,并让共享状态变为就绪以提醒future
}
 
int main()
{
    // 演示用 promise<int> 在线程间传递结果。
    std::vector<int> numbers = { 1, 2, 3, 4, 5, 6 };
    std::promise<int> accumulate_promise;
    std::future<int> accumulate_future = accumulate_promise.get_future();
    std::thread work_thread(accumulate, numbers.begin(), numbers.end(),
                            std::move(accumulate_promise));
    accumulate_future.wait();  //等待结果
    std::cout << "result=" << accumulate_future.get() << '\n';
    work_thread.join();  //阻塞等待线程执行完成
 
    getchar();
    return 0;
}

std::promise< T >对象的成员函数get_future()产生一个std::future< T >对象,代码示例中已经展示了future对象的两个方法:wait()与get(),下面给出更多操作函数供参考:

值得注意的是,std::future< T >在多个线程等待时,只有一个线程能获取等待结果。当需要多个线程等待相同的事件的结果(即多处访问同一个共享状态),需要用std::shared_future< T >来替代std::future < T >,std::future< T >也提供了一个将future转换为shared_future的方法f.share(),但转换后原future状态失效。这有点类似于智能指针std::unique_ptr< T >与std::shared_ptr< T >的关系,使用时需要留心。

2.3使用packaged_task与future传递结果

除了为一个任务或线程提供一个包含共享状态的变量,还可以直接把共享状态包装进一个任务或线程中。这就需要借助std::packaged_task< Func >来实现了,其具体用法如下:

std::packaged_task< Func >构造时绑定一个函数对象,也产生一个未就绪的共享状态。通过thread启动或者仿函数形式启动该函数对象。但是相比promise,没有提供set_value()公用接口,而是当执行完绑定的函数对象,其执行结果返回值或所抛异常被存储于能通过 std::future 对象访问的共享状态中。继续使用上面的程序示例,改为使用packaged_task传递结果,修改后的代码如下:

//future3.cpp 使用packaged_task传递被调用线程返回结果,通过共享状态变化通知调用线程已获得结果

#include <vector>
#include <thread>
#include <future>
#include <numeric>
#include <iostream>
#include <chrono>
 
int accumulate(std::vector<int>::iterator first,
                std::vector<int>::iterator last)
{
    int sum = std::accumulate(first, last, 0);
    return sum;
}
 
int main()
{
    // 演示用 packaged_task 在线程间传递结果。
    std::vector<int> numbers = { 1, 2, 3, 4, 5, 6 };
    std::packaged_task<int(std::vector<int>::iterator,std::vector<int>::iterator)> accumulate_task(accumulate);
    std::future<int> accumulate_future = accumulate_task.get_future();
    std::thread work_thread(std::move(accumulate_task), numbers.begin(), numbers.end());
    accumulate_future.wait();  //等待结果
    std::cout << "result=" << accumulate_future.get() << '\n';
    work_thread.join();  //阻塞等待线程执行完成
 
    getchar();
    return 0;
}

一般不同函数间传递数据时,主要是借助全局变量、返回值、函数参数等来实现的。上面第一种方法使用全局变量传递数据,会使得不同函数间的耦合度较高,不利于模块化编程。后面两种方法分别通过函数参数与返回值来传递数据,可以降低函数间的耦合度,使编程和维护更简单快捷。

2.4 使用async传递结果
前面介绍的std::promise< T >与std::packaged_task< Func >已经提供了较丰富的异步编程工具,但在使用时既需要创建提供共享状态的对象(promise与packaged_task),又需要创建访问共享状态的对象(future与shared_future),还是觉得使用起来不够方便。有没有更简单的异步编程工具呢?future头文件也确实封装了更高级别的函数std::async,其具体用法如下:

std::future std::async(std::launch policy, Func, Args…)
std::async是一个函数而非类模板,其函数执行完后的返回值绑定给使用std::async的std::futrue对象(std::async其实是封装了thread,packged_task的功能,使异步执行一个任务更为方便)。Func是要调用的可调用对象(function, member function, function object, lambda),Args是传递给Func的参数,std::launch policy是启动策略,它控制std::async的异步行为,我们可以用三种不同的启动策略来创建std::async:

std::launch::async参数 保证异步行为,即传递函数将在单独的线程中执行;
std::launch::deferred参数 当其他线程调用get()/wait()来访问共享状态时,将调用非异步行为;
std::launch::async | std::launch::deferred参数 是默认行为(可省略)。有了这个启动策略,它可以异步运行或不运行,这取决于系统的负载。
继续使用上面的程序示例,改为使用std::async传递结果,修改后的代码如下:

//future4.cpp 使用async传递被调用线程返回结果

#include <vector>
#include <thread>
#include <future>
#include <numeric>
#include <iostream>
#include <chrono>
 
int accumulate(std::vector<int>::iterator first,
                std::vector<int>::iterator last)
{
    int sum = std::accumulate(first, last, 0);
    return sum;
}
 
int main()
{
    // 演示用 async 在线程间传递结果。
    std::vector<INT >番号= { 123456 }。
    オートaccumulate_future = STD ::非同期(STD ::打ち上げ::非同期、蓄積、numbers.begin()、numbers.end())。        // 自動可以自动推断变量的类型 
    のstd :: coutの<< " 結果= " << accumulate_future。取得()<< ' \ nを' ; 
 
    getchar関数(); 
    リターン 0 ; 
}

STDを使用することができます::大幅に簡略化されより少ないプログラミングの労力非同期(async)、我々は内部のスレッドの作成の詳細に焦点を当てないように、上記のコードからわかるように、あなたはまた、スレッド作成戦略を指定することができ、簡単に非同期実行状況や結果にアクセスすることができるようになります。そこで、我々は、スレッドを作成するにはstd ::非同期の代替を使用し、非同期操作を行うために私たちの最初の選択肢とさせることができます。

 

おすすめ

転載: www.cnblogs.com/lx17746071609/p/11128255.html