マルチスレッド処理の基礎(3) - クラス設計に関する留意事項 Part 1
少し間が空いてしまいましたが、これから数回に分けて、マルチスレッド処理の設計・実装における注意点を解説していきます。
まずは、クラス設計に関する留意事項を説明します。
スレッドセーフなクラスとスレッドセーフでないクラスを分離し、明確化する
スレッドセーフなクラスは作成が難しいため、アプリケーション固有の処理を行うクラスでスレッドセーフを保つような設計は、基本的には避けます。通常は標準ライブラリをそのまま利用し、標準ライブラリに無い機能が必要であれば別途ライブラリとして作成し、それを利用するかたちでスレッドセーフを実現するようにします(後述する「不変クラス」は例外)。
なお、UMLで設計ドキュメントを作成する場合は、標準的な記法ではありませんが、ステレオタイプを用いると設計者の意図が読み手に伝わりやすくなります。
補足:.NET Frameworkの標準ライブラリ
マルチスレッド処理に関連する.NET Frameworkの標準ライブラリには多くのものがあります。学ぶ際には、APIリファレンスを読む前に、以下の解説記事から読むことをお勧めします。
.NET での並列プログラミング | Microsoft Docs
値の保持を目的とするクラスは、可能な限り不変にする
フィールドがすべて変更不可能となっているクラスを、不変(Immutable)クラスと呼びます。不変クラスのインスタンスは、排他制御等の特別な処理を行わなくても、すべてのスレッドから安全にアクセスすることが可能になります。
情報の更新回数が多いものを不変クラスとした場合はオブジェクト生成コストの面で不利になる場合がありますが、更新より読み取り回数の方が多い場合には、排他制御が不要になることでパフォーマンスの向上が図れる場合があります。
以下に、変更不可能なフィールドとして扱ってよい例とサンプルコードを示します。
- readonly修飾子つきの組み込みデータ型(int, double, stringなど)
- readonly修飾子つきの不変なクラス・構造体型(DateTime, TimeSpanなど、自作のクラス・構造体でもよい)
- readonly修飾子つきの組み込みデータ型・クラス・構造体のReadOnlyCollection(※ただし、コンストラクタの引数を直接利用する場合は引数を一旦コピーしたうえでReadOnlyCollectionを生成する必要がある)
// 不変な値を表すサンプルクラス。実装を簡略化するため引数のチェック処理等は省略。 public class ImmutableValue { private readonly int id; private readonly string name; private readonly ReadOnlyCollection<byte> data; public ImmutableValue(int id, string name, IEnumerable<byte> data) { this.id = id; this.name = name; this.data = data.ToList().AsReadOnly(); } public int ID { get { return id; } } public string Name { get { return name; } } public ReadOnlyCollection<byte> Data { get { return data; } } }
補足:.NET Frameworkの不変コレクションライブラリ
現時点ではベータ版ですが、Microsoft社製の不変コレクションライブラリがNuGetから入手可能です。
NuGet Gallery | Microsoft.Bcl.Immutable 1.1.32-beta
参考記事:.NETが不変になる
補足:ドメイン駆動設計における不変なクラス
マルチスレッド処理の文脈以外のソフトウェア設計手法の文脈においても、不変なオブジェクトの概念は重要な役割を果たします。
たとえば、ドメイン駆動設計においては、「値オブジェクト」という名称で、アプリケーションの構成要素の一部を不変なクラスのかたちで取り扱うことの有用性が紹介されています。
エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)
- 作者: エリック・エヴァンス,今関剛,和智右桂,牧野祐子
- 出版社/メーカー: 翔泳社
- 発売日: 2011/04/09
- メディア: 大型本
- 購入: 19人 クリック: 1,360回
- この商品を含むブログ (131件) を見る