LoginSignup
4
6

More than 5 years have passed since last update.

[C#備忘録]<独断と偏見まとめ>MultiThreadデザインパターンのC#版について

Last updated at Posted at 2017-08-26

前回の投稿の独断と偏見に満ちたまとめをする

注意

完璧に独断と偏見です。

MultiThreadデザインパターンごとのまとめ

ここに書くのは、基本的には個人的なパターンの使いどころ。
必要であれば補足する。

1. Single Thread Execution パターン

  • メソッドを、スレッドセーフなもの/そうでないもので区別するだけ
    • スレッドセーフでないものはlockさせましょうねー
  • 概念であり、デザインパターンとは言えないだろう

2. Immutable パターン

  • クラスを、メンバ等の変化がないもの/そうでないもので区別するだけ
    • メンバ等の変化ないものは、クラス自体がスレッドセーフといえる
  • これも概念であり、デザインパターンとは言えないだろう

3. Guarded Suspension パターン

  • スレッド間メッセージキューのパターン
    • メッセージがない間は、受け取り側スレッドを待機させる
    • BlockingCollectionを使えば、何も考えなくてもメッセージキューが作れる
補足
  • 本当の意味は、受け取り側にガード条件を設けて、ガード条件に合致しない場合は、スレッドを待ちにさせる。
  • ガード条件が「メッセージが存在するか」という最もシンプルな場合は、上記のメッセージキューに当てはまる。
    • ガード条件が複雑な場合は、Wait/Pulseを駆使する必要がある。
    • けど、キューは排他機能付きのConcurrentQueueBlockingCollectionを使うべし。

4. Balking パターン

  • Guarded Suspension パターンの亜種
  • ガード条件に合致しないときは、処理を中止する
    • 中止することで、パフォーマンス向上を図る
  • Guarded Suspension パターンに、不要だったら処理を中止するという機能を付加するというイメージ

5. Producer-Consumer パターン

  • Guarded Suspension パターンで、メッセージの送り手と受け手の両方にガード条件が存在するパターン
    • 例えば、メッセージキューでキューの上限が決まっていて、送り手側でキューが上限に達していたら待つ処理を追加するときに用いる
  • つまり、Guarded Suspension パターンに機能を付加するイメージ

6. Read-Writer Lock パターン

  • 2種類のlockを使用して排他制御する
    • 書き込み用ロック:ロック中は、書き込みも読み込みもできない
    • 読み込み用ロック:ロック中は、読み込みはできるが、書き込みはできない
  • ロックが不要な処理に対してはロック条件を緩くすることで、パフォーマンス向上を図る
  • ReaderWriterLockSlimを使用すれば容易に実現できる

7. Thread-Per-Message パターン

  • たぶん、受け取り用常駐スレッドが存在しないただの非同期処理
    • 非同期処理で実行した結果は特に待たない
    • つまり、Task.Run()してawaitWaitしないパターン
  • デザインパターンとして考えなくてよいだろう

8. Woker Thread パターン

  • Producer-Consumerパターンのスレッドプール使用版
  • C#ではスレッドはTask=スレッドプールを使用するのがデファクトスタンダードなので、Producer-Consumerパターンと同一視して問題ないと思う

9. Future パターン

  • 非同期処理だけど、クライアント側の好きなタイミングで結果を受け取る
    • 待ち受ける際、すぐにクライアント側に制御が返る(引換券)
    • 結果は後から受け取る
  • async/awaitのことと思ってたけど、必ずしもそうではない。
    • 開始と待ち受けが、異なるメソッドの時はTask.StartTask.Waitという使用する。
  • クライアント側はマルチスレッドを意識しない(あたかもシングルスレッドで動作しているかのよう)というメリットもある
補足

一応補足しておくと、FutureパターンはThread-Per-Messageパターンの亜種

10. Two-Phase Termination パターン

  • スレッド終わるときに、後始末をしてから終わるようにすること
    • 2段階の終了処理
  • あたりまえのこと。スレッドのたしなみ
  • わざわざデザインパターンという程のものではない

11. Thread-Specific Storage パターン

  • 使わない。不要。
    • スレッド固有のコインロッカー。だけど、C#では基本的にスレッドプールで処理する(=スレッドを使いまわす)ので、スレッド固有という概念が使えない
    • そもそも、使えなくても何の問題もない

12. Active Object パターン

  • FutureパターンWokerThreadパターンを組み合わせたもの
  • ごちゃごちゃしているけど、結果的にFutureパターンとの違いは、
    「固定されたスレッドがワーカーとして実処理を行う」ということ
  • つまり、ワーカースレッドはシングルスレッドで処理する必要がある(例えばスレッドセーフにできないとか)ときに使える
    • クライアントスレッドへはすぐに引換券(疑似結果)が返ってくる。実際の結果は、クライアントスレッド側の好きなタイミング受け取る。
      • 受け取りフラグみたいなのを、ワーカースレッド側でONにして、フラグONでクライアント側は結果格納先にアクセスできるようにする
    • クライアントスレッドはマルチスレッドで動作させて、複数クライアントに対応できる

まとめのまとめ

以上より、強引にまとめると、覚えておくべきパターンは以下の4通り

  • WorkerThreadパターン
    • スレッド間での同期処理
    • 必要であればBalkingの機能を追加する
  • Futureパターン
    • 非同期処理をしつつも、クライアント側の好きなタイミングで結果を受け取る
  • ActiveObjectパターン
    • Futureパターンにしたいが、処理するスレッドが1つに限定される場合にWorkerThreadパターンを融合する
  • Read-Writer Lockパターン
    • 書き込みと読み込みが存在するときの排他制御

(Two-Phase Terminationパターンは重要だが、当たり前のことなのでカウントしていない)

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6