Upload
tatsuki-shimizu
View
440
Download
0
Embed Size (px)
Citation preview
async async のの 2 2 種類の種類の overload overload
default
task のpolicy 指定
async(F&&, Args&&...);
async(std::launch, F&&, Args&&...);
std::launchstd::launch
enum class lauch : unspecified { async = unspecified , deferred = unspecified implementation-defined };
scoped enum を⽤用いて規定されている bitmask type
拡張 policy 及びそのbitmask の実装は,実装系に許可されている
std::launch std::launch とと std::asyncstd::async
lauch::async
lauch::deferred
異なるスレッドで⾮非同期実⾏行を指定する
呼び出しスレッドと同期実⾏行を指定する
launch::deferred 指定時get /wait が呼ばれたときに task が実⾏行される実⾏行されないかもしれない(thread と違って問題ない)
注釈について注釈について
auto f = std::async( std::launch::deferred, []{ std::cout << "wait..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); return 0; });
std::shared_future< int > sf = f.share(); auto sf2 = sf;
sf.wait(); std::cout << sf2.get() << std::endl;
sf2 に対して wait/get 呼ぶ前からsf2 の参照する shared_state は結果を持っている
(shared_state に関しては item38 参照)
default default とと policy policy 指定指定
std::async( f );
std::async( std::launch::async | std::launch::deferred, f);
default の実⾏行は bit を async/deferred 両⽅方⽴立てるのと同じ意味
つまり,次の2つは全く同じ挙動をする
このときの挙動は実装依存
とある
“ Note: If this policy is specified together with other policies, such aswhen using a policy value of launch::async |launch::deferred,
implementations should defer invocation or the selection of the policywhen no more concurrency can be effectively exploited.
実装依存!!とはいえ [futures.async] の note に
> 実⾏行ポリシーを launch::async |launch::deferred と指定したときには, 並列性を効果的に利⽤用できない場合,どちらのポリシーに従うかを決めるのを遅延するか,task の実⾏行を遅延する(deferred を選択する)べき
というところ並列性が⼗十分に取れるときの動きは書かれていない(未規定??)
http://melpon.org/wandbox/permlink/PiUr8pUjjDmA7C5V
std::cout << "hardware_concurrency = " << std::thread::hardware_concurrency()<<std::endl; using namespace std::literals; std::vector< std::future< void > > fvec(100); std::generate( std::begin(fvec), std::end(fvec), []{ return std::async([]{ std::this_thread::sleep_for(1s); std::cout << std::this_thread::get_id() <<std::endl;});}); std::for_each( std::begin(fvec), std::end(fvec), [](auto&& x){ x.wait();} );
なお,gcc は 6 系で default の挙動が変わる模様??6 より前と後で,次の code の結果がかなり違う
にあるように,同期/⾮非同期的に動くかを library の実装が決めてくれる
Bartos Mileski ⽒氏のblog
default policy default policy の良さの良さ
terminate called after throwing an instance of 'std::system_error' what(): Resource temporarily unavailable 中⽌止
さらに,resource のこういう error ⾒見なくてよくなる
default default ,困ります,困ります(>_<)(>_<)
auto fut = srd::async( f ); // use default policy
s, t, u という名前に次のようにスレッドを束縛する
s f が実⾏行されるスレッドt async が呼ばれるスレッドu get/wait を呼ぶスレッド
例えば次みたいな code を考えると...
void f() { std::cout << "s: " << std::this_thread::get_id() << std::endl; // s } std::future< void > g() { std::cout << "t: " << std::this_thread::get_id() << std::endl; // t auto fut = std::async( f ); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ return fut; } int main() { std::cout << "u:" << std::this_thread::get_id() << std::endl; // u std::async( g ).get().get(); return 0; }
http://melpon.org/wandbox/permlink/a7oHldjWHTEGA1Yq
VisualStudio2015 で build した結果
あれ,あれ,s s とと t t が⼀一致しているが⼀一致している!!(!!(環境でまちまち環境でまちまち))
default default ,困ります,困ります(>_<)(>_<)
s != t かわからない (∵ deferred になるかもしれない s != u かわからない そもそもget/wait されないかもしれない
s f が実⾏行されるスレッドt async が呼ばれるスレッドu get/wait を呼ぶスレッド
*再掲
default default ,困ります,困ります(>_<)(>_<)thread-local storage(TLS) を使ってると問題出る
thread_local int i = 0;
void fun(int left, int right ) { if (left < right-1) { auto f1 = std::async( fun, left, (left + right) / 2); auto f2 = std::async( fun, (left + right)/2, right); f1.wait(); f2.wait(); } else { ++i; } }
int main() { fun(0, 10); std::cout << i << std::endl; }
どのスレッドで fun が呼ばれるかで結果が変わるhttp://melpon.org/wandbox/permlink/lcd7FOlnXfsUBH7T
default default ,困ります,困ります(>_<)(>_<)
次の code を考える
void f () { std::this_thread::sleep( 1s ); // std::chrono::seconds(1)
auto fut = std::async(f); while (fut.wait_for(100ms) // std::chrono::milliseconds(1) != std::future_status::ready) { /* something */ }
wait_for/untilwait_for/until とと future_statusfuture_status
deferred 関数をもつ future_status::deferred
準備完了(値が返せる) future_status::ready
準備中 future_status::timeout
wait_for/until の返値は shared_stateにより以下で決まる
while (fut.wait_for(100ms) != std::future_status::ready) { }
deferred と schedule されたら,いくら待っても ready にならない!!!! bug だっ
bug fixbug fixfuture が deferred かどうか直接聞く⽅方法はないwait の返値で future_status::deferred と⾔言うのはあるwait は deferred のとき何もしない
template< typename R > inline bool is_defered( std::future< R > const& f) { return f.wait_for(0s) == std::future_status::deferred; } // shared_future も まったく同じなので割愛
auto fut = std::async(f); if (is_defered(fut)) { /* hogehoge */ } else { while (fut.wait_for(100ms)) { /* fugafuga */ } }
ということなので, check を作る
async async でデフォルトが使える条件でデフォルトが使える条件1. call/wait を呼ぶ thread と並列であってもなくてもよい
2. どの thread の TLS を読み書きするか問わない(またはTLS は使わない)
3. get/wait をどの path でも呼んでいる.または実⾏行されなくても問題ない保証がある
4. wait_for/until を使っているならば deferred も考慮している
であれば async をdefault で実⾏行してもよい.
どれか⼀一つでも⽋欠けるなら default ,やめよう
async policy async policy を使うを使う API API ⽤用意する⽤用意する......
template< typename F, typename ...Args > inline std::future< typename std::result_of< F(Args...) >::type > reallyAsync( F&& f, Args&&... args) { return std::async(std::launch::async, std::forward< F >(f), std::forward< Args >(args)...); }
c++11 version1
(c++11では)間違ってないけど...C++14 だと若干型の宣⾔言が修正されている
template< typename F, typename ...Args > inline std::future< std::result_of_t< std::decay_t< F >(std::decay_t< Args >...) > > // ^^^^^^^^^^^^ ^^^^^^^^^^^^ // decay がついてる!! reallyAsync( F&& f, Args&&... args) { return std::async(std::launch::async, std::forward< F >(f), std::forward< Args >(args)...); }
愚直に書き換え愚直に書き換え
c++14 version1
返値の型書くの,ツライ(-_-;)
修正版修正版(C++14 version)(C++14 version)
template< typename F, typename ...Args > inline auto reallyAsync( F&& f, Args&&... args) { return std::async(std::launch::async, std::forward< F >(f), std::forward< Args >(args)...); }
すっきり (^^)/
* C++11 で trailing return type 使っても,やっぱりだるい...