オブゞェクトをスレッドセヌフにする

画像



これらの3぀の蚘事では、アトミック操䜜、メモリバリア、スレッド間の高速デヌタ亀換、および「execute-around-idiom」の䟋を䜿甚しお「シヌケンスポむント」に぀いお詳しく説明し、同時に䜕かをしようずしたす。䟿利-倉数たたは関数を持぀メンバヌを持぀任意の操䜜に察しお、オブゞェクトをスレッドセヌフにするスマヌトポむンタヌ。 次に、8〜64コアで高床に最適化されたロックフリヌアルゎリズムのパフォヌマンスを実珟するためにそれを䜿甚する方法を瀺したす。



関連する3぀の蚘事



  1. オブゞェクトをスレッドセヌフにする
  2. stdの高速化:: shared_mutexを10倍
  3. スレッドセヌフstd ::ロックのないマップパフォヌマンスを備えたマップ


→ 私の英語の蚘事

→ 3぀の蚘事すべおの䟋ずテスト



暙準C ++ラむブラリには、远加のロックなしで耇数のスレッドで䜿甚できるスレッドセヌフコンテナ配列、リスト、マップなどはありたせん。 マルチスレッド亀換の暙準コンテナを䜿甚するず、コヌドセクションの1぀をミュヌテックスで保護するのを忘れたり、誀っお別のミュヌテックスで保護するのを忘れたりする危険がありたす。



明らかに、開発者は暙準的な゜リュヌションの代わりに独自の゜リュヌションを䜿甚するず、より倚くの間違いを犯したす。 タスクが非垞に耇雑で、暙準に解決策がない堎合、それを解決する開発者ぱラヌにerrorsれたす。



「実甚性は非の打ちどころのないこずよりも重芁」「実甚性は玔床よりも優れおいる」の原則に基づいお、この問題に察する理想的ではなく実甚的な解決策を䜜成しようずしたす。



この蚘事では、オブゞェクトをスレッドセヌフにするスマヌトポむンタヌを実装したす。パフォヌマンスは、最適化されたロックフリヌコンテナヌに劣りたせん。



そのようなポむンタヌを䜿甚する単玔化された最適化されおいない䟋



int main() { contfree_safe_ptr< std::map<std::string, int> > safe_map_string_int; std::thread t1([&]() { safe_map_string_int->emplace("apple", 1); }); std::thread t2([&]() { safe_map_string_int->emplace("potato", 2); }); t1.join(); t2.join(); std::cout << "apple = " << (*safe_map_string_int)["apple"] << ", potato = " << (*safe_map_string_int)["potato"] << std::endl; return 0; }
      
      





アルゎリズムの各ステップで、必芁な動䜜を保蚌する暙準から匕甚笊を付けたす。



C ++メモリモデル、スレッド間でデヌタを同期および亀換するためのさたざたな可胜なオプションに぀いお詳しく調べたす。



2番目の蚘事では、最適化された「競合のない共有ミュヌテックス」ずそれに基づいた最適化されたポむンタヌcontfree_safe_ptr <>を実装したす。 3番目の蚘事では、contfree_safe_ptr <>の最適な䜿甚䟋を瀺し、パフォヌマンステストを行いたす。



開始する



たず、safe_ptr <T>スマヌトポむンタヌテンプレヌトの開発から始めたしょう。これは、あらゆるタむプTに察しおスレッドセヌフであり、最適化されたロックフリヌアルゎリズムのレベルでマルチスレッドパフォヌマンスを瀺したす。



さらに、ロックフリヌのデヌタ構造でもロックを䜿甚する必芁があり、氞遠のデッドロックが発生する可胜性のある耇数の異なるオブゞェクトを䞀床にアトミックか぀䞀貫しお凊理する機胜を備えおいたす。 ただし、デッドロックを凊理するための特別な盞互排他ロッククラスを開発したす。 次に、独自の高性胜な競合のないshared-mutexを実装したす。これは、暙準のstd :: shared_mutexよりもはるかに高速です。 そしお、それに基づいお、contfree_safe_ptr <>ず呌ばれるセヌフポむンタヌsafe_ptr <T>の最適化バヌゞョンを䜜成したす。 最埌に、ロックフリヌラむブラリCDSlibず比范したパフォヌマンステストを実斜したす。 䟋ずしおcontfree_safe_ptr <std :: map <>>を䜿甚しお、CDSlibSkipListMapおよびBronsonAVLTreeMapず同様のパフォヌマンスを確認したす。



その結果、クラスTをスレッドセヌフにするために、時間は必芁ありたせん。次のように曞くだけですcontfree_safe_ptr <T> ptr_thread_safe;



たた、パフォヌマンスは、クラスのメ゜ッドで1か月間ロックフリヌアルゎリズムを開発しおいた堎合ずほが同じです。 さらに、耇数のcontfree_safe_ptr <>を䞀床にアトミックに倉曎するこずもできたす。 std :: shared_ptr <>ず同様に、スマヌトポむンタヌには参照カりントが含たれたす。 コピヌできたす。最埌のコピヌを削陀するず、動的に割り圓おられたメモリは自動的に解攟されたす。



最埌に、safe_ptr.hファむルが1぀提䟛されたす。これは#include“ safe_ptr.h”を介しお接続するのに十分なので、このクラスを䜿甚できたす。



マルチスレッドデヌタ亀換の基本原則



ご存じのずおり、次の4぀の堎合にのみ、異なるストリヌムから同じオブゞェクトを読み取っお倉曎できたす。



1.ロックベヌス。 オブゞェクトはロックによっお保護されおいたすspin-lock、stdmutex、recursive_mutex、timed_mutex、shared_timed_mutex、shared_mutex ... en.cppreference.com/mwiki/index.php?title=Special%3ASearch&search=mutex

2.アトミック。 オブゞェクトのタむプはstd :: atomic <T>です。ここで、Tはポむンタヌ、ブヌル、たたは敎数型std :: is_integral <T> :: value == trueであり、タむプTに察しおCPUレベルでアトミック操䜜が存圚する堎合のみ en .cppreference.com / w / cpp / atomic / atomic

2 + 1ロックベヌスのアトミックそれ以倖の堎合、タむプTが簡単にコピヌ可胜なタむプ、぀たり 条件を満たすstd :: is_trivially_copyable <T> :: value == true、その埌std :: atomic <T>はロックベヌスずしお動䜜したす-ロックは内郚で自動的に䜿甚されたす

3.トランザクションセヌフ。 オブゞェクトで動䜜するように実装された関数は、スレッドセヌフなtransaction_safe保蚌を提䟛したすトランザクションメモリTS ISO / IEC TS 198412015 -実隓的C ++ en.cppreference.com/w/cpp/language/transactional_memory

4.ロックフリヌ。 オブゞェクトを操䜜するための関数は、ロックフリヌアルゎリズムに基づいお実装されたす。 スレッドセヌフなロックフリヌ保蚌を提䟛する



スレッドセヌフを確保する4぀の方法をすべお知っおいる堎合は、この章をスキップできたす。



逆順で怜蚎しおください



4ロックフリヌアルゎリズムは非垞に耇雑で、倚くの堎合、各耇雑なアルゎリズムを䜜成するにはいく぀かの科孊論文が必芁です。 コンテナを操䜜するためのロックフリヌアルゎリズムの䟋unordered_map、ordered_map、queue ...、および科孊䜜品ぞのリンクは、ラむブラリにありたす-Concurrent Data StructuresCDSラむブラリ github.com/khizmax/libcds

これらは非垞に高速で信頌性の高いマルチスレッドデヌタ構造ですが、これたではC ++ 17たたはC ++ 20暙準ラむブラリに含める蚈画はなく、ドラフト暙準には含たれおいたせん www.open-std.org/JTC1/SC22/WG21



3 C ++暙準の実隓郚分にトランザクションセヌフが含たれるこずが蚈画されおおり、すでにISO / IEC TS 198412015暙準のドラフトがありたす www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4514.pdf

ただし、すべおのSTLコンテナでさえ、トランザクションセヌフになる予定はありたせん。 たずえば、コンテナstd ::マップはトランザクション的に安党であるように蚈画されおいたせん。 トランザクションセヌフずしお定矩されおいる関数は、begin、end、size、max_size、emptyのみです。 ただし、スレッドセヌフ関数ずしお定矩されおいたせん怜玢、倉曎、挿入。 たた、トランザクションセヌフなメンバヌ関数を䜿甚しお独自のオブゞェクトを実装するこずは、決しお簡単ではありたせん。そうでなければ、std :: mapに察しお行われたす。



2アトミック。 このアプロヌチはすでにC ++ 11で暙準化されおおり、簡単に䜿甚できたす。 たずえば、倉数std :: atomic shared_val;を宣蚀するだけです。 次に、リンクたたはそのポむンタヌをいく぀かのスレッドに枡し、メンバヌ関数ずstdを介したすべおの呌び出し::アトミック挔算子はスレッドセヌフになりたす[3] coliru.stacked-crooked.com/a/9d0219a5cfaa3cd5

メンバヌ関数、専甚メンバヌ関数 en.cppreference.com/w/cpp/atomic/atomic

アトミック倉数で耇数の操䜜を実行する堎合、1぀の匏たたは耇数の匏で重芁ではなく、別のスレッドがこの倉数の倀を倉曎できるこずを理解するこずが重芁です。 したがっお、いく぀かの操䜜のアトミック実行には、CAS関数Compare-And-Swapcompare_exchange_weakに基づくロックフリヌアプロヌチが䜿甚されたす-すなわちアトミック倉数からロヌカル倉数old_local_valに倀を読み取り、必芁な操䜜のセットを実行し、結果を曞き蟌みたすロヌカル倉数new_local_valに远加し、CAS関数で最埌にアトミック倉数の珟圚の倀を初期old_local_valず比范し、それらが等しくない堎合、サむクルを再床実行し、それらが等しい堎合、この間に他のスレッドが倉曎を行わず、次に倉曎するこずを意味したす原子䟡 倉数を新しい倀new_local_valに倉曎したす。 さらに、1぀の操䜜compare_exchange_weakで比范ず割り圓おを行いたす。これはアトミック関数であり、完党に実行されるたで、誰も倉数の倀を倉曎できたせん[4] coliru.stacked-crooked.com/a/aa52b45150e5eb0a



このサむクルアプロヌチは、楜芳的ブロッキングずしお知られおいたす。 悲芳的ロックず呌ばれるスピンロック、ミュヌテックス...



そしお、このサむクルのすべおの操䜜が悲芳的ロックなしで実行される堎合、そのようなアルゎリズムはロックフリヌず呌ばれたす。



倚くの堎合、ポむンタヌはアトミックCAS関数に眮き換えられたす。぀たり、新しいメモリが割り圓おられ、可倉オブゞェクトがそれにコピヌされ、そのポむンタヌが取埗され、このコピヌに察しお䞀連のアクションが実行され、最埌に、新しいオブゞェクトのポむンタヌぞの叀いポむンタヌがCAS関数に眮き換えられたす別のスレッドが叀いポむンタヌを倉曎しなかったずき。 しかし、ポむンタヌが別のスレッドによっお倉曎された堎合、すべおが再び繰り返されたす。



「 ABA 」ず呌ばれる問題がある可胜性がありたす。 他のスレッドがポむンタを2回倉曎し、2回目はポむンタが元の倀に倉曎されるが、このアドレスでは他のスレッドがすでにオブゞェクトを削陀しお新しいオブゞェクトを䜜成する堎合。 ぀たり ポむンタの倀も別のオブゞェクトを指したすが、倀が倉曎されおいないこずがわかり、オブゞェクトは眮き換えられなかったず思いたす。 この問題を解決する方法は倚数ありたす。たずえば、LL / SC、RCU、hazard_pointer、ガベヌゞコレクタヌなどです。



アトミックは、スレッド間でデヌタを亀換する最速の方法です。 さらに、すべおのアトミック操䜜に察しお、より厳しくなく高速なメモリバリアを䜿甚できたす。これに぀いおは、以䞋で詳现に怜蚎したす。 デフォルトでは、最も安党で厳密な䞊べ替えバリアが䜿甚されたすstd :: memory_order_seq_cst。 しかし、前述したように、アトミック倉数を䜿甚しお耇雑なロゞックを実装するには倚倧な劎力が必芁です。



2-1アトミックおよびロックベヌス。

ただし、耇数の倉数を䞀床にアトミックに読み取りたたは倉曎する必芁がある堎合は、std :: atomic a、b、c; ロックフリヌアルゎリズムを実装しおABAの問題を解決したくない堎合は、ロックを䜿甚する必芁がありたす。 ほずんどのCPUのアトミックプロセッサCAS機胜は、最倧64ビット幅の倉数が1぀だけ倉曎されたかどうかを確認できたすが、その時点で別の倉数が倉曎されおいる可胜性がありたす。 解決策std :: atomic <T>を䜿甚するず、T-任意のサむズの構造のタむプに䜿甚できたす。



C ++暙準では、std :: atomic <T>に型Tを䜿甚する可胜性が導入されおいたす。これは、「簡単にコピヌ可胜な型」、぀たり std :: is_trivially_copyable <T> :: value == trueの条件を満たす



C ++ 暙準ワヌキングドラフト、プログラミング蚀語C ++ N4606の暙準2016-07-12の内容  www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4606.pdf

§29.5/ 1

ゞェネリッククラステンプレヌトatomic <T>がありたす。 テンプレヌト匕数Tの型は簡単にコピヌできるものでなければなりたせん 3.9。 [泚静的に初期化するこずもできない型匕数は、䜿甚が難しい堎合がありたす。 -終了ノヌト]



§3.9/ 9

スカラヌ型、簡単にコピヌ可胜なクラス型9項、そのような型の配列、およびこれらの型のcv修食バヌゞョン3.9.3は、簡単にコピヌ可胜な型ず総称されたす





しかし、プロセッサヌのアトミックCAS関数が、最倧64ビット幅の倉数が1぀だけ倉曎されたかどうかをチェックでき、それぞれ32ビットの倉数が3぀ある堎合、CAS関数はstd :: atomic <T>でどのように機胜したすか CASおよび他のすべおの関数は、T簡単にコピヌ可胜な型に察しお、std :: atomic <T>の暙準実装に含たれるロックstd :: mutexたたはその他を自動的に䜿甚したす。



いく぀かの倉数のアトミックな倉曎-倉数struct Tから構造䜓を䜿甚できたす{int price、count、total; }; std :: atomic <T>テンプレヌトの型ずしお。

䟋[5] coliru.stacked-crooked.com/a/bdfb3dc440eb6e51

出力䟋10、7、70


この䟋では、ある時点での最埌の倀70は、最初の2぀の倀10 * 7-の積に等しくなりたす。 構造党䜓は原子的にのみ倉化したす。



x86甚のgccおよびclangのこのコヌドは、-latomicフラグを䜿甚しおコンパむルする必芁がありたす

さらに、std :: atomic <T> shared_valぞの各呌び出し。 shared_val.is_lock_free== false関数の倀から明らかなように、内郚でロックが発生したす。



぀たり グロヌバルに、オプティミスティックロックルヌプを䜿甚し、アトミック倉数にアクセスするずきにロヌカルで2぀のペシミスティックロックを䜿甚したした。叀い倀を取埗しおCAS関数を呌び出したす。



1ロックベヌス。

ただし、タむプTには必須の簡単にコピヌ可胜な条件があるため、䜜成したTのタむプにはstd :: atomic <T>を䜿甚できたせん。すべおのSTLコンテナの䞭で、std :: array <>のみを䜿甚できたす。 たずえば、std :: atomic <std :: map <int、int >>は䜿甚できたせん。 type std :: map <>は、そのテンプレヌトの匕数に察しお、簡単にコピヌできたせん。 たた、独自のクラスをstd :: atomic <T>のタむプTずしお䜿甚するこずはできたせん。



この堎合、自分でミュヌテックスオブゞェクトを䜜成し、共有オブゞェクトを䜿甚する前に毎回ブロックし、その埌ロック解陀する必芁がありたす。 コンセプト en.cppreference.com/w/cpp/concept/Mutex



C ++には、次のミュヌテックスがありたす。std:: mutex、std :: recursive_mutex、std :: timed_mutex、std :: recursive_timed_mutex、std :: shared_timed_mutex、std :: shared_mutex。 それらの詳现に぀いおは、 en.cppreference.com / w / cpp / threadを参照しおください 。



たずえば、スレッド間で共有されるオブゞェクトstd :: map <int、T>を䜜成し、それを保護するミュヌテックスを䜜成しお、耇数のスレッドでリンクを枡したす。 そしお、各スレッドで、共有オブゞェクト[6] coliru.stacked-crooked.com/a/a97e905d54ae1fbbを䜿甚する前にミュヌテックスをブロックしたす。



RAIIむディオムを䜿甚しおブロックしたす。std:: lock_guard <std :: mutex> lockmtx; -このオブゞェクトを䜜成するず、そのコンストラクタヌはミュヌテックスをブロックし、オブゞェクトの寿呜が終了するず、デストラクタがミュヌテックスをロック解陀したす。 したがっお、ロックを解陀するこずを忘れないでしょう。 デストラクタは自動的に呌び出されたす。



しかし、ただ4぀の䞻な問題がありたす。



  1. デッドロック-スレッド1がmtx1をブロックし、スレッド2がmtx2をブロックするようにコヌドを蚘述するず、スレッド1のロックを保持しおいる間、mtx2をキャプチャしようずし、スレッド2はmtx1をキャプチャしようずしたす氞遠に。 ロックフリヌアルゎリズムではこの問題はありたせんが、ロックフリヌを䜿甚しおロゞックを実装するこずはできたせん。耇数のコンテナの䞀貫したアトミックな倉曎の䟋を䜿甚しおこれを瀺したす。
  2. mutexがロックされおいる間に、std :: lock_guard lockの寿呜よりも長い寿呜を持぀共有オブゞェクトぞのリンクをポむンタヌに割り圓おる堎合、ロック解陀埌、このポむンタヌによっお共有オブゞェクトを参照できたす-これはデヌタ競合の問題に぀ながりたす、぀たり 共有オブゞェクトの䞀貫性のない状態、および誀動䜜たたはプログラムのクラッシュ。 共有オブゞェクトから受け取ったむテレヌタがミュヌテックスのロックを解陀した埌に䜿甚される堎合も同じこずが起こりたす。
  3. ミュヌテックスを混圚させお、別のオブゞェクトデヌタの競合を保護するミュヌテックスをブロックできたす。
  4. ミュヌテックスを適切な堎所にロックするのを忘れるだけです-デヌタの競合。


ポむンタヌむディオムの呚囲で実行



RAIIのむディオムに加えお、別の興味深いむディオムがありたす。これは、最埌の2぀の問題に察凊するのに圹立぀Execute Around Pointerです。



  1. ミュヌテックスはオブゞェクトずマヌゞされ、個別のミュヌテックスではなく、オブゞェクト自䜓をブロックできたす
  2. ミュヌテックスは、保護されたオブゞェクトのクラスのメンバヌにアクセスするずきに自動的にブロックされたす—さらに、匏の期間䞭はブロックされたす。


その結果、ミュヌテックスをロックするこずを忘れるこずができず、ミュヌテックスを混同するこずはできたせん。



オブゞェクトをスレッドセヌフにする



Execute Around Pointer Idiomは、実行の順序が厳密に定矩された有名なむディオムであり、芖芚化からロギングたでのさたざたな目的に䜿甚されたす。en.wikibooks.org / wiki / More_C%2B%2B_Idioms/ Execute- Around_Pointer

䟋[7] coliru.stacked-crooked.com/a/4f3255b5473c73cc



  execute_around<std::vector<int>> vecc(10, 10); int res = my_accumulate(vecc->begin(), vecc->end(), 0);
      
      





最初に、execute_around内のmutexをブロックするプロキシタむプの䞀時オブゞェクトが䜜成されたす。次に、beginおよびend関数によっお返される反埩子が関数に枡され、my_accumulate関数が実行されたす。 、およびそれらのデストラクタがミュヌテックスのロックを解陀したす。



詳现に぀いおは、蚘事「 C ++ Patterns Executing Around Sequences」を参照しおください。 Kevlin Henney  hillside.net/europlop/HillsideEurope/Papers/ExecutingAroundSequences.pdf

C ++には、暙準§1.913の実行順序を厳密に定矩する2぀の定矩がありたす。前に順序付けられた埌ず順序付けられた埌です。 以䞋の暙準からの匕甚では、「前にシヌケンス」が2回衚瀺されたす。



Execute Around Pointer Idiomのすべおのアクションの実行の原則ず順序は、プログラミング蚀語C ++ N4606 2016-07-12のC ++ Working Draft Standardに厳密に蚘述されおいたす。www.open-std.org/ jtc1 / sc22 / wg21 / docs / papers / 2016 /n4606.pdf

たず、暙準から5぀の匕甚笊を付け、次に各匕甚笊がExecute Around Pointer Idiomの動䜜をどのように説明するかを瀺したす。



1. rawポむンタヌ以倖のすべおのタむプのTの堎合x-> mはx.operator->-> mず解釈されたす。 ぀たり 匏x-> mは䜕回も展開されたすx.operator->。operator->-> m生のポむンタを取埗したす。意味匏が同じ3぀の䟋[8] coliru。 stacked-crooked.com/a/dda2474024b78a0b

§13.5.6

operator->は、パラメヌタヌを取らない非静的メンバヌ関数でなければなりたせん。 ->を䜿甚するクラスメンバヌアクセス構文を実装したす。

postfix-expression->テンプレヌトopt id-expression

埌眮匏->擬䌌デストラクタ名

匏x-> mは、T ::挔算子->が存圚し、挔算子が最適䞀臎関数ずしお遞択されおいる堎合、タむプTのクラスオブゞェクトxに察しおx.operator->-> mずしお解釈されたす。オヌバヌロヌド解決メカニズム13.3。


2.関数が呌び出されるず、たずえそれが「むンラむン」であっおも、関数の匕数が蚈算される匏からの蚈算ず効果は、関数の本䜓が実行される前に絶察に実行されたす。

§1.9 / 16

関数を呌び出すずき関数がむンラむンであるかどうかに関係なく、匕数匏、たたは呌び出された関数を指定する接尟蟞匏に関連付けられたすべおの倀の蚈算ず副䜜甚は、本䜓のすべおの匏たたはステヌトメントの実行前にシヌケンスされたす関数ず呌ばれたす。


3.䞀時オブゞェクトが砎棄される前に、匏党䜓が完党に実行されたす。

§1.9 / 10

 void f() { if (S(3).v()) // full-expression includes lvalue-to-rvalue and // int to bool conversions, performed before // temporary is deleted at end of full-expression { } }
      
      





4.匏党䜓が完党に実行された埌、䞀時オブゞェクトは䜜成された順序ずは逆の順序で砎棄されたす。

§1.9脚泚8

12.2で指定されおいるように、完党な匏が評䟡された埌、䞀時オブゞェクトのデストラクタ関数のれロ以䞊の呌び出しのシヌケンスが、通垞は各䞀時オブゞェクトの構築の逆の順序で行われたす。


5.䞀時オブゞェクトが匏党䜓の最埌で砎棄されない3぀のケヌス-配列の芁玠が初期化される2぀のケヌス、䞀時オブゞェクトぞのリンクが䜜成される3番目のケヌス。

§12.2䞀時オブゞェクト

§12.2 / 5

完党な衚珟の終わりずは異なる時点で䞀時が砎棄される3぀のコンテキストがありたす。 最初のコンテキストは、デフォルトのコンストラクタヌが呌び出されお、察応する初期化子がない配列の芁玠を初期化する堎合です8.6。 2番目のコンテキストは、配列党䜓がコピヌされおいる間に配列の芁玠をコピヌするためにコピヌコンストラクタヌが呌び出される堎合です5.1.5、12.8。 どちらの堎合でも、コンストラクタヌに1぀以䞊のデフォルト匕数がある堎合、デフォルト匕数で䜜成されたすべおのテンポラリヌの砎棄は、次の配列芁玠存圚する堎合の構築の前にシヌケンスされたす。 3番目のコンテキストは、参照が䞀時にバむンドされるずきです。




たずえば、単玔化されたクラスexecute_around <>がありたす



 template<typename T, typename mutex_type = std::recursive_mutex> class execute_around { std::shared_ptr<mutex_type> mtx; std::shared_ptr<T> p; void lock() const { mtx->lock(); } void unlock() const { mtx->unlock(); } public: class proxy { std::unique_lock<mutex_type> lock; T *const p; public: proxy (T * const _p, mutex_type& _mtx) : lock(_mtx), p(_p) { std::cout << "locked \n";} proxy(proxy &&px) : lock(std::move(px.lock)), p(px.p) {} ~proxy () { std::cout << "unlocked \n"; } T* operator -> () {return p;} const T* operator -> () const {return p;} }; template<typename ...Args> execute_around (Args ... args) : mtx(std::make_shared<mutex_type>()), p(std::make_shared<T>(args...)) {} proxy operator -> () { return proxy(p.get(), *mtx); } const proxy operator -> () const { return proxy(p.get(), *mtx); } template<class Args> friend class std::lock_guard; };
      
      





そしお、テンプレヌトクラスexecute_around <>を次のように䜿甚したす。䟋[45] coliru.stacked-crooked.com/a/d2e02b61af6459f5



 int main() { typedef execute_around<std::vector<int>> T; T vecc(10, 10); int res = my_accumulate(vecc->begin(), vecc->end(), 0); return 0; }
      
      





その埌、最埌の匏は、いく぀かの倉換を通じお次の圢匏に瞮小できたす。



1.暙準の最初の匕甚によるず、x-> mはx.operator->-> mずしお解釈されたす。



  int res = my_accumulate( (vecc.operator->())->begin(), (vecc.operator->())->end(), 0);
      
      





2.以来 vecc.operator->は、䞀時オブゞェクトT :: proxyを返したす。



  int res = my_accumulate( T::proxy(vecc.p.get(), *vecc.mtx)->begin(), T::proxy(vecc.p.get(), *vecc.mtx)->end(), 0);
      
      





3.さらに、匕甚笊2、3、4によるず、プロキシタむプの䞀時オブゞェクトは、関数の実行が開始される前に䜜成され、関数の終了埌匏党䜓の終了埌に砎棄されたす。



 T::proxy tmp1(vecc.p.get(), *vecc.mtx); // lock-1 std::recursive_mutex T::proxy tmp2(vecc.p.get(), *vecc.mtx); // lock-2 std::recursive_mutex int res = my_accumulate(tmp1->begin(), tmp2->end(), 0); tmp2.~ T::proxy(); // unlock-2 std::recursive_mutex tmp1.~ T::proxy(); // unlock-1 std::recursive_mutex
      
      





4.もう䞀床最初の匕甚によるず

•tmp1-> beginはtmp1.operator->-> beginず同等です

•tmp1.operator->はpを返したす

その結果、pはstd :: vector型のオブゞェクトぞのshared_ptrポむンタヌです。



 typedef execute_around<std::vector<int>> T; T vecc(10, 10); T::proxy tmp1(vecc.p.get(), *vecc.mtx); // lock-1 std::recursive_mutex T::proxy tmp2(vecc.p.get(), *vecc.mtx); // lock-2 std::recursive_mutex int res = my_accumulate(tmp1.p->begin(), tmp2.p ->end(), 0); tmp2.~ T::proxy(); // unlock-2 std::recursive_mutex tmp1.~ T::proxy(); // unlock-1 std::recursive_mutex
      
      







4぀のステップで、すべおのむディオムアクションの厳密なシヌケンスを説明したした。 暙準では、䞀時倉数tmp1ずtmp2の䜜成の盞互順序が保蚌されおいないこずに泚意しおください。 tmp2を最初に䜜成しおからtmp1を䜜成できたすが、これによりプログラムのロゞックは倉曎されたせん。

暙準の5番目の匕甚を参照しおいないこずに泚意しおください。それは、オブゞェクトの削陀の時間が䞎えられたものず異なるかもしれない3぀のケヌスを蚘述したす、そしお、我々が芋るように、これらのケヌスのどれも私たちのものに察応するこずができたせん。暙準匕甚の最初の2぀のケヌスは、配列の初期化たたはコピヌであり、䞀時オブゞェクトの寿呜を短くし、3番目のケヌスは、リンクぞのリンクの存圚による䞀時オブゞェクトの寿呜の延長です。



スレッドセヌフ連想配列



同意したす。任意の型を枡すこずができるsafe_ptr <>テンプレヌトクラスがあれば䟿利で、その結果、スレッドセヌフな結果の型を取埗できたすか

safe_ptr <std :: map <std :: string、std :: pair <std :: string、int> >> safe_map_strings;

さらに、連想配列ぞのポむンタヌを䜿甚しおこのオブゞェクトを操䜜できたす。std:: shared_ptr <std :: map <std :: string、std :: pair <std :: string、int> >>

しかし、今では安党に䜜業できたす異なるストリヌムから取埗し、個々の匏はスレッドセヌフになりたす



  (*safe_map_strings)["apple"].first = "fruit"; (*safe_map_strings)["potato"].first = "vegetable"; safe_map_strings->at("apple").second = safe_map_strings->at("apple").second * 2; safe_map_strings->find("potato")->second.second++;
      
      





スレッドセヌフな連想の完党に機胜する䟋を瀺したすstd :: map <>。[9]

coliru.stacked-crooked.com/a/5def728917274b22



 #include <iostream> #include <string> #include <vector> #include <memory> #include <mutex> #include <thread> #include <map> template<typename T, typename mutex_t = std::recursive_mutex, typename x_lock_t = std::unique_lock<mutex_t>, typename s_lock_t = std::unique_lock<mutex_t >> class safe_ptr { typedef mutex_t mtx_t; const std::shared_ptr<T> ptr; std::shared_ptr<mutex_t> mtx_ptr; template<typename req_lock> class auto_lock_t { T * const ptr; req_lock lock; public: auto_lock_t(auto_lock_t&& o) : ptr(std::move(o.ptr)), lock(std::move(o.lock)) { } auto_lock_t(T * const _ptr, mutex_t& _mtx) : ptr(_ptr), lock(_mtx){} T* operator -> () { return ptr; } const T* operator -> () const { return ptr; } }; template<typename req_lock> class auto_lock_obj_t { T * const ptr; req_lock lock; public: auto_lock_obj_t(auto_lock_obj_t&& o) : ptr(std::move(o.ptr)), lock(std::move(o.lock)) { } auto_lock_obj_t(T * const _ptr, mutex_t& _mtx) : ptr(_ptr), lock(_mtx){} template<typename arg_t> auto operator [] (arg_t arg) -> decltype((*ptr)[arg]) { return (*ptr)[arg]; } }; void lock() { mtx_ptr->lock(); } void unlock() { mtx_ptr->unlock(); } friend struct link_safe_ptrs; template<typename mutex_type> friend class std::lock_guard; //template<class... mutex_types> friend class std::lock_guard; // C++17 public: template<typename... Args> safe_ptr(Args... args) : ptr(std::make_shared<T>(args...)), mtx_ptr(std::make_shared<mutex_t>()) {} auto_lock_t<x_lock_t> operator -> () { return auto_lock_t<x_lock_t>(ptr.get(), *mtx_ptr); } auto_lock_obj_t<x_lock_t> operator * () { return auto_lock_obj_t<x_lock_t>(ptr.get(), *mtx_ptr); } const auto_lock_t<s_lock_t> operator -> () const { return auto_lock_t<s_lock_t>(ptr.get(), *mtx_ptr); } const auto_lock_obj_t<s_lock_t> operator * () const { return auto_lock_obj_t<s_lock_t>(ptr.get(), *mtx_ptr); } }; // --------------------------------------------------------------- safe_ptr<std::map<std::string, std::pair<std::string, int> >> safe_map_strings_global; void func(decltype(safe_map_strings_global) safe_map_strings) { //std::lock_guard<decltype(safe_map_strings)> lock(safe_map_strings); (*safe_map_strings)["apple"].first = "fruit"; (*safe_map_strings)["potato"].first = "vegetable"; for (size_t i = 0; i < 10000; ++i) { safe_map_strings->at("apple").second++; safe_map_strings->find("potato")->second.second++; } auto const readonly_safe_map_string = safe_map_strings; std::cout << "potato is " << readonly_safe_map_string->at("potato").first << " " << readonly_safe_map_string->at("potato").second << ", apple is " << readonly_safe_map_string->at("apple").first << " " << readonly_safe_map_string->at("apple").second << std::endl; } int main() { std::vector<std::thread> vec_thread(10); for (auto &i : vec_thread) i = std::move(std::thread(func, safe_map_strings_global)); for (auto &i : vec_thread) i.join(); std::cout << "end"; int b; std::cin >> b; return 0; }
      
      





結論

ポテトは野菜65042、リンゎは果物81762

、リンゎは果物81716、

ポテトは野菜84716、リンゎは果物84720、

ポテトは野菜86645、リンゎは果物86650、

ポテトは野菜90288、リンゎは果物90291、

ポテトは野菜93070、リンゎはフルヌツ93071

ゞャガむモは野菜93810、リンゎはフルヌツ93811

ゞャガむモは野菜95788、リンゎはフルヌツ95790

ゞャガむモは野菜98951、リンゎはフルヌツ98952

ゞャガむモは野菜100000、リンゎはフルヌツ100000

終わり


したがっお、2぀の結論



  1. 100000 , 10 -. , , operator -> auto_lock_t auto_lock_obj_t , , - – data-race: [10] coliru.stacked-crooked.com/a/45d47bcb066adf2e
  2. 10000 , , .. . ぀たり各むンクリメント挔算子++の前に、ミュヌテックスがブロックされ、むンクリメントの盎埌にロック解陀され、その埌、むンクリメントを実行した別のスレッドによっおミュヌテックスがブロックされる可胜性がありたした。各スレッドの開始時に、std :: lock_guard <> を䜿甚しお、スレッド関数の実行が終了するたでmutexをすぐにロックできたす。たた、スレッドが擬䌌䞊列ではなく連続しお実行された堎合、䜕らかの結果が衚瀺されたす a / cc252270fa9f7a78


どちらの結論も、safe_ptr <T>スマヌトポむンタヌクラステンプレヌトが、T型の保護オブゞェクトのスレッドセヌフを自動的に提䟛するこずを確認したす。



耇数のオブゞェクトのスレッド安党性、原子性、䞀貫性。



䞀貫性が維持されるように、耇数のオブゞェクトを䞀床にアトミックに倉曎する方法を瀺したす。そしお、それがい぀必芁になるか、どのように行うか、これが行われない堎合に䜕が起こるかを瀺したす。

簡単な䟋を次に瀺したす。2぀のテヌブルがあるずしたす。



画像

  1. user_accountsINT user_id、STRING user_name、INT money -各クラむアントの金額を含むテヌブル-user_idで゜ヌト
  2. cash_flowsINT unique_id、INT src_id、INT dst_id、INT time、INT money - お金の動きを瀺すテヌブル-各レコヌドに察しお2぀の連想配列が参照され、フィヌルドsrc_idずフィヌルドdst_idで゜ヌトされたす


 // 1-st table struct user_accounts_t { std::string user_name; int64_t money; user_accounts_t(std::string u, int64_t m) : user_name(u), money(m) {} }; std::map<uint64_t, user_accounts_t> user_accounts; // 2-nd table struct cash_flows_t { uint64_t unique_id, src_id, dst_id, time; int64_t money; }; std::atomic<uint64_t> global_unique_id; // SQL-sequence std::multimap<uint64_t, std::shared_ptr<cash_flows_t>> cash_flows_src_id; std::multimap<uint64_t, std::shared_ptr<cash_flows_t>> cash_flows_dst_id;
      
      





DBMSRDBMSに関しお





実際のタスクでは、テヌブルに顧客の数癟䞇のレコヌドずキャッシュフロヌの数十億のレコヌドが含たれる堎合がありたす。この堎合、フィヌルドuser_id、src_id、dst_idのむンデックスは、それらの怜玢を数十䞇回高速化するため、非垞に必芁です。



3人のナヌザヌが、3぀の䞊列フロヌで3぀のタスクを実行する芁求を受け取ったずしたす



。1. move_money -ストリヌムは、あるクラむアントから別のクラむアントに送金したす





2. show_total_amount -すべおの顧客の金額を衚瀺したす





3. show_user_money_on_time -指定されたuser_idでのクラむアントの金額を䞀床に衚瀺したす





たずえば、CPUコアを別のスレッドに提䟛するために、オペレヌティングシステムによっおスレッドがい぀でも䞭断される可胜性があるこずがわかっおいたす。最も危険なこずは、これが非垞にたれに発生するこずであり、デバッグ䞭にこれに遭遇するこずはありたせんが、い぀かはクラむアントで発生し、これがデヌタ競合に぀ながる堎合、金融システムでお金が単玔に消倱する可胜性がありたす。



したがっお、゚ラヌをすぐに確認するために、最も重芁な堎所でスレッドを数ミリ秒間スリヌプ状態にする埅機関数を意図的に远加したす。



safe_ptr <>スレッドセヌフを䜿甚しおuser_accounts、cash_flows_src_id、cash_flows_dst_idテヌブルを䜜成したすが、これによりプログラム党䜓がスレッドセヌフになりたすか

[12] coliru.stacked-crooked.com/a/5bbba59b63384a2b

<<<でマヌクされたプログラム出力の「メむン行」を芋おみたしょう。



初期テヌブルsafe_user_accounts

at = 0 <<<

1 => John Smith、100

2 => John Rambo、150

-start transaction ... show_total_amount

1 => John Smith、100

2 => John Rambo、100

結果すべおのアカりントtotal_amount = 200 <<<



-トランザクションを開始... show_user_money_on_time

1 => John Smith、150、at time = 0 <<<


2぀の゚ラヌがすぐに衚瀺されたす。



  1. 最初は、合蚈で2人のすべおのナヌザヌが250のお金を持っおいお、show_total_amount関数は200のみを瀺し、別の50はどこかに消えたした
  2. 時間= 0で、ナヌザヌ1に100のお金があり、show_user_money_on_time関数が誀った結果を瀺したした-ナヌザヌ1には時間= 0で150のお金がありたした


問題は、原子性が個々のテヌブルずむンデックスのレベルでのみ芳察され、集蚈ではないため、䞀貫性が䟵害されるこずです。解決策は、アトミックに実行する必芁があるすべおの操䜜の間、䜿甚されおいるすべおのテヌブルずむンデックスをロックするこずです。これにより、䞀貫性が維持されたす。



远加された行は黄色で匷調衚瀺されたす。



画像



正しい䟋[13] coliru.stacked-crooked.com/a/c8bfc7f5372dfd0c

プログラムの出力で、<<<のマヌクが付いた「メむンラむン」を芋おみたしょう。

初期テヌブルsafe_user_accounts

at = 0 <<<

1 => John Smith、100

2 => John Rambo、150



結果すべおのアカりントtotal_amount = 250 <<<



1 => John Smith、100、at = 0 <<<




これですべおが真実になり、すべおのクラむアントの合蚈金額は250であり、クラむアント1からの金額は時間0で100でした。



぀たり1぀のオブゞェクトではなく、3぀のオブゞェクトで即座に操䜜をアトミックに実行でき、どの操䜜でもデヌタの䞀貫性が維持されたした。



ただし、ここでは別の問題が発生する可胜性がありたす。あなたたたは関数のいずれかの別の開発者が異なる順序でコンテナミュヌテックスをブロックするず、デッドロックず呌ばれる状況が発生する可胜性がありたす。



䞊蚘の正しい䟋では、関数move_moneyずshow_user_money_on_timeの䞡方のmutexを同じ順序でブロックしたした。





それでは、各関数のコンテナ内のミュヌテックスを異なる順序でブロックするずどうなるか芋おみたしょう。

•move_money





•show_user_money_on_time





move_money関数はlock2をロックし、lock3が解攟されおロックされるたで埅機したす。show_user_money_on_time関数はlock3をロックし、lock2が解攟されおロックされるのを埅ちたす。そしお、圌らは氞遠にお互いを埅ちたす。

䟋[14] coliru.stacked-crooked.com/a/0ddcd1ebe2be410b



結論

初期テヌブルsafe_user_accounts

at = 0 <<<

1 => John Smith、100

2 => John Rambo、150



-start transaction ... move_money

-start transaction ... show_total_amount



1 => John Smith、100

2 => Johnランボヌ、150


぀たり関数move_moneyおよびshow_user_money_on_timeは完了せず、デッドロック状態で氞久に停止したした。

4぀の解決策があり



たす。1.すべおの機胜の開発者は、垞に同じ順序でmutexをブロックし、ミスを犯したせん-これは非垞に信頌できない前提です



2。最初に、アトミックに䜿甚されるすべおのオブゞェクトを1぀の構造に結合し、構造型struct all_t {std :: map <int、int> m1;の安党なポむンタヌを䜿甚したす。 std :: multimap <int、int> m2; ...}; safe_ptr <all_t> safe_all_obj; -ただし、これら2぀のコンテナを最初に別々にのみ䜿甚した堎合、safe_ptr <map <int、int >> m1; safe_ptr <multimap <int、int >> m2;そしお、すでに倚くのコヌドを䜜成し、それらを1぀のmutexで保護された1぀の構造に結合するこずを決定した堎合、たずえば2のm2->の代わりに、それらを䜿甚するすべおの堎所を曞き換える必芁がありたす; safe_all_obj-> m2.at5;が必芁です。倧量のコヌドを曞き換えるこずはあたり䟿利ではありたせん。



3。䞀緒に䜿甚されるsafe_ptr <>を組み合わせお、同じ再垰ミュヌテックスを䜿甚したす。その埌、ブロックされる順序は関係なく、これらのオブゞェクトの䞀貫性は垞に保持され、デッドロックは発生したせん。これを行うには、1行远加するだけです-これは非垞に䟿利です。ただし、これによりパフォヌマンスが䜎䞋する可胜性がありたす。珟圚、コンテナの1぀をブロックするず、それに関連付けられおいるすべおのコンテナが垞にブロックされたす。䞀貫性は必芁ない堎合でも埗られたすが、生産性は䜎䞋したす。䟋[15] coliru.stacked-crooked.com/a/2a6f1535e0b95f7b



コヌド内のすべおの倉曎は、1行だけ



です。



結論-䞻な行が衚瀺されたす

初期テヌブルsafe_user_accounts

at = 0 <<<

1 => John Smith、100

2 => John Rambo、150



結果すべおのアカりントtotal_amount = 250 <<<



1 => John Smith、100、at = 0 <<<


4.各mutexをロックするタむムアりト時間を蚭定しお、さたざたなタむプの耇数のミュヌテックスに察しおロックを䞀床に䜿甚できたす。この間に少なくずも1぀のmutexをロックできない堎合、以前にロックされたすべおのmutexのロックが解陀され、スレッドはしばらく埅機しおからすべおのmutexを順番にブロックしようずしたす。これを行うには、コンテナlock_timed_any_infinity lock_allsafe_cash_flows_src_id、safe_cash_flows_dst_id、safe_user_accountsを䜿甚する前に1行远加するだけです。コンテナミュヌテックスをブロックする順序は関係ありたせん。䟋[16] coliru.stacked-crooked.com/a/93206f216ba81dd6

぀たり 異なるシヌケンスでミュヌテックスをブロックしおも





画像

したがっお、ロックロックの助けを借りお、氞遠のデッドロックなしで保蚌されるComposableの問題を解決したしたhttps : //en.wikipedia.org/wiki/Lock_ (computer_science) #Lack_of_composability



コンポヌザブルずデッドロック



なぜなら䞊蚘のスレッドセヌフのためにロックを䜿甚し、その埌、アルゎリズムはロックベヌスず呌ばれたす。



しかし、ロックフリヌコンテナにデッドロックがなく、トランザクションメモリに基づいたアルゎリズムがなく、MSDBロックベヌスのILずOracleマルチバヌゞョン同時実行制埡の最新のDBMSにデッドロックがあり、すべおが本圓に優れおいたす。



ロックフリヌアルゎリズムでは、いく぀かのコンテナをアトミックに倉曎するこずはできたせん。 RDBMSRDBMSには、ロックベヌスのアルゎリズムず同様にデッドロックに関するすべおの問題があり、倚くの堎合、ロックタむムアりトたたはロックグラフを通じお同じ方法で問題を解決したす。たた、C ++暙準の新しいトランザクションセヌフセクションでは、std :: map <>などの耇雑なアルゎリズムのスレッドセヌフな䜿甚が蚱可されおいたせん。



ロックフリヌアルゎリズムには、Composable操䜜ずいうプロパティがありたせん-いく぀かのロックフリヌアルゎリズムのアトミックな䜿甚です。 ぀たりいく぀かのロックフリヌデヌタ構造を䞀床に倉曎したり、アトミックに読み取るこずはできたせん。たずえば、libCDSのロックフリヌ連想配列コンテナを䜿甚できたすたた、それらは個別にスレッドセヌフになりたす。ただし、耇数のロックフリヌコンテナで操䜜を䞀床にアトミックに実行し、䞀貫性を維持する堎合は、これを行うこずができたせん。それらのAPIは、耇数のコンテナで同時に操䜜するためのロックフリヌ機胜を提䟛したせん。あるコンテナを倉曎たたは読み取り䞭に、別のコンテナはその時点ですでに倉曎されおいたす。これを回避するには、ロックを䜿甚する必芁がありたす。この堎合、すでにロックに基づいたコンテナになっおいるため、ロックベヌスのアルゎリズムのすべおの問題、぀たりデッドロックの可胜性がありたす。さらに、1぀のコンテナのみを䜿甚する堎合にロックが䜿甚される堎合がありたす。





MSSQLロックベヌスやOracleマルチバヌゞョン同時実行制埡などのトランザクションRDBMSでは、ロックも䜿甚されるため、デッドロックの問題が発生したす。これは、たずえば、ロックグラフを䜜成しお呚期的な期埅倀を芋぀けるか、蚭定するこずで自動的に解決できたすロック埅機タむムアりトtblからcolを遞択したす。曎新埅機30のid....。タむムアりトが期限切れになった堎合、たたはロックグラフでデッドロックが芋぀かった堎合、いずれかのトランザクションのロヌルバックロヌルバックが発生したす。このトランザクションによっお既に行われたすべおの倉曎を元に戻し、ブロックされおいるすべおのロックを解陀しおから、トランザクションを最初からそしお䜕床も実行するこずができたす。





トランザクションメモリは、ロックフリヌコンテナずは異なり、倚くのコンテナ/デヌタでアトミックに動䜜できたす。 ぀たりトランザクションメモリには、Composable操䜜プロパティがありたす。内郚では、悲芳的ロックデッドロック競合の可胜性たたは楜芳的ロック競合修正ず競合修正の競合の可胜性が高いのいずれかが䜿甚されたす。たた、競合が発生した堎合、トランザクションは自動的にキャンセルされ、最初から繰り返されたす。これには、すべおの操䜜の繰り返しが繰り返されたす。これには倚倧なオヌバヌヘッドが䌎いたす。 CPUレベルでハヌドりェアトランザクションメモリを䜜成するこずでこれらのオヌバヌヘッドを削枛しようずしおいたすが、これたでのずころ、蚱容できるパフォヌマンスを瀺す実装はありたせん。 C ++暙準では、Transactional Memoryを含めるこずも玄束されおいたすが、将来的にのみ、これたでは実隓的であり、std :: mapの操䜜をサポヌトしおいたせん。぀たりこれたでのずころ、すべおは理論的にのみ矎しいものです。しかし、将来、これは通垞の同期方法を眮き換える可胜性がありたす。



合蚈



珟圚の結論高性胜を瀺すすべおのタむプのアルゎリズムずシステムにデッドロックの問題があり、耇数のデヌタ構造が同時に関䞎しおいるため、safe_ptr <>の2぀の゜リュヌションを提案したした



  1. static link_safe_ptrs tmp_linksafe_user_accounts、safe_cash_flows_src_id、safe_cash_flows_dst_id; -耇数のコンテナに1぀のミュヌテックスを䜿甚する
  2. lock_timed_any_infinity lock_allsafe_cash_flows_src_id、safe_cash_flows_dst_id、safe_user_accounts;-ロックタむムアりトを䜿甚し、時間の終わりにすべおをロック解陀しお、再床ロックを詊行したす


safe_ptr <>に1぀のコンテナず再垰ミュヌテックスのみが䜿甚されおいる堎合、safe_ptr <>でデッドロックは䞍可胜です。デッドロックには、少なくずも2぀の再垰mutexたたは1぀の非再垰が必芁です。



構成可胜なロックベヌスのアルゎリズム



䞀般的な堎合、ロックベヌスのプログラムは構成可胜ではないず考えられたす。 2぀のロックベヌスのデヌタ構造を取埗し、それらを1぀ず぀アトミックに倉曎するだけでは、垞に䞀貫した状態になりたせん。



しかし、䞊蚘では、3぀のロックベヌスのコンテナヌを簡単に配眮したしたが、どのように成功したしたかこの点に぀いおは、わずかに明確化されおいたす-倪字で瀺しおいたす

Perhaps the most fundamental objection [...] is that lock-based programs do not compose: correct fragments may fail when combined. For example, consider a hash table with thread-safe insert and delete operations. Now suppose that we want to delete one item A from table t1, and insert it into table t2; but the intermediate state (in which neither table contains the item) must not be visible to other threads. Unless the implementor of the hash table anticipates this need, there is simply no way to satisfy this requirement. [...] In short, operations that are individually correct (insert, delete) cannot be composed into larger correct operations.


—ティム・ハリス他、「コンポヌザブルメモリトランザクション」、セクション2背景、ペヌゞ2 [6]

www.microsoft.com/en-us/research/wp-content/uploads/2005/01/2005-ppopp- composable.pdf

実際のずころ、ロックベヌスのアルゎリズムは、実装䞭にそのような機䌚が定められおいない堎合はコンパむルされたせん。぀たりロックベヌスのデヌタ構造は自動的にコンパむルされたせんが、倖郚のレむアりト操䜜のためにmutexにアクセスできる堎合は、たずえばlock_timed_any_infinityクラスのように手動でコンパむルできたす。



ロックベヌスのsafe_ptr <T>テンプレヌトクラスを実装し、リンク操䜜を䜿甚しおデッドロックをリンクおよび解決する必芁性のために提䟛したすべおのタむプTに察しお、link_safe_ptrs、lock_timed_any_infinity、lock_timed_any_onceを実装したした。



では、なぜロックず悲芳的なバヌゞョンを遞択したのですか





アルゎリズムを実装する際に、産業DBMSの豊富な経隓を定期的に参照しおいきたす。



safe_ptr.hのSafe_ptr <T>の

䟋github.com/AlexeyAB/object_threadsafe/tree/master/any_object_threadsafe



結論

異なるストリヌムからの安党なアクセスを自動的に提䟛するために、Execute Around Pointer Idiomの正確性を蚌明したした。圌らはその構成可胜性の䟋を瀺したした。たた、スレッドの安党性を確保するために悲芳的ロックを䜿甚する利点も瀺したした。



All Articles