不変性
簡単に言えば、初期化後に変更されない場合、メモリは不変です。
C、Java、C# などの命令型言語で記述されたプログラムは、メモリ内データを自由に操作できます。いったん取っておかれた物理メモリの領域は、プログラムの実行中いつでも、実行スレッドによって全体または一部が変更される可能性があります。実際、命令型言語はこのようなプログラミング方法を奨励しています。
この方法でプログラムを作成することは、シングルスレッド アプリケーションでは非常に成功しています。しかし、最新のアプリケーション開発が 1 つのプロセス内で複数の同時実行スレッドに移行するにつれて、潜在的な問題と複雑さの世界が導入されています。
実行スレッドが 1 つしかない場合、この 1 つのスレッドがメモリ内のすべてのデータを「所有」しているため、自由に操作できると考えることができます。ただし、複数の実行スレッドが関与する場合、所有権の暗黙の概念はありません。
代わりに、この負担はプログラマーにかかっており、プログラマーはメモリ内構造がすべてのリーダーに対して一貫した状態であることを保証するために多大な労力を費やす必要があります。ロック構造は、別のスレッドによって更新されている間、あるスレッドがデータを参照できないように注意して使用する必要があります。この調整がなければ、スレッドは更新の途中にあるデータを消費することは避けられません。このような状況の結果は予測不可能であり、しばしば壊滅的です。さらに、コード内でロックを正しく機能させることは非常に困難であり、下手をするとパフォーマンスが低下したり、最悪の場合、デッドロックが発生して実行が回復不能になる可能性があります。
不変のデータ構造を使用すると、複雑なロックをコードに導入する必要がなくなります。プログラムの存続期間中にメモリのセクションが変更されないことが保証されている場合、複数のリーダーがメモリに同時にアクセスできます。その特定のデータを一貫性のない状態で観察することはできません。
Lisp、Haskell、Erlang、F#、Clojure などの多くの関数型プログラミング言語は、その性質上、不変のデータ構造を推奨しています。ますます複雑化するマルチスレッド アプリケーション開発や多数のコンピュータを使用するコンピュータ アーキテクチャに移行するにつれて、彼らへの関心が再び高まっているのはこのためです。
州
アプリケーションの状態は、特定の時点でのすべてのメモリと CPU レジスタの内容と簡単に考えることができます。
論理的には、プログラムの状態は次の 2 つに分けられます。
- ヒープの状態
- 実行中の各スレッドのスタックの状態
C# や Java などのマネージド環境では、あるスレッドが別のスレッドのメモリにアクセスすることはできません。したがって、各スレッドはそのスタックの状態を「所有」します。structスタックは、値型 ( ) のローカル変数とパラメーター、およびオブジェクトへの参照を保持していると考えることができます。これらの値は、外部スレッドから分離されています。
ただし、ヒープ上のデータはすべてのスレッド間で共有可能であるため、同時アクセスを制御するには注意が必要です。すべての参照型 ( class) オブジェクト インスタンスはヒープに格納されます。
OOP では、クラスのインスタンスの状態はそのフィールドによって決定されます。これらのフィールドはヒープに格納されるため、すべてのスレッドからアクセスできます。コンストラクターの完了後にフィールドを変更できるメソッドをクラスが定義している場合、そのクラスは可変です (不変ではありません)。フィールドを変更できない場合、型は不変です。readonlyすべての C# /Javafinalフィールドを持つクラスが必ずしも不変であるとは限らないことに注意することが重要です。これらの構造は、参照が変更できないことを保証しますが、参照されるオブジェクトは変更できません。たとえば、フィールドにはオブジェクトのリストへの変更不可能な参照がある場合がありますが、リストの実際の内容はいつでも変更できます。
型を真に不変であると定義することにより、その状態は凍結されていると見なすことができるため、その型は複数のスレッドから安全にアクセスできます。
実際には、すべての型を不変として定義するのは不便な場合があります。不変型の値を変更するには、かなりの量のメモリ コピーが必要になる場合があります。一部の言語は、このプロセスを他の言語よりも簡単にしますが、どちらの方法でも、CPU は余分な作業を行うことになります。メモリのコピーに費やされた時間がロックの競合の影響を上回るかどうかを判断するには、多くの要因が影響します。
リストやツリーなどの不変データ構造の開発については、多くの研究が行われています。このような構造、たとえばリストを使用する場合、「追加」操作は、新しいアイテムが追加された新しいリストへの参照を返します。前のリストへの参照には変更は見られず、データの一貫したビューが引き続き表示されます。