私の現在のアプリケーションは、オブジェクトの単一インスタンスを多くの主要コンポーネントのグローバル変数として使用していますが、これは依存性注入を使用するよりも劣っていると考えられています。
将来は自分のアプリケーションをオープン ソースにしたいと考えていますが、最初にコードをリファクタリングして、チーム コラボレーションに最も推奨される手法を使用し、他の開発者が自分のソース コードをより簡単に変更できるようにしたいと考えています。
共有リソースの例: CFML 言語では、サーバー スコープがあります。これは、サーバー インスタンス全体のすべての要求に使用できる共有メモリです。
サーバー スコープへの変更を管理するための新しい設計コンセプトは次のとおりです。
- サーバー スコープへの書き込みおよび読み取り用のインターフェイスを提供する ServerMemoryManager という名前のコンポーネントの単一インスタンスを作成します。
- サーバー スコープにアクセスする必要があるその他のコードには、init() 関数または setServerMemoryManager() 関数を介して ServerMemoryManager の単一インスタンスへの参照が挿入されます。
- コンポーネントが ServerMemoryManager オブジェクトに対してデータの読み取り/書き込みを行うときは常に、サーバー スコープを内部的にロックできるため、2 つのスレッドがサーバー スコープ内の同じメモリに同時に書き込むことはできません。
これは、スレッドセーフにするためにロックが必要な共有リソース (共有メモリ、ファイルシステムなど) を管理するための最良の方法ですか?
ベスト プラクティスと見なされる特定の読み取り/書き込み操作中にロックを必要とする共有リソースを管理するために使用できる追加の方法について説明してください。
編集: scope="server" をロックする代わりに、受け入れられた回答に基づいて、名前付きロックを使用し、よりきめ細かいロックで共有リソースを管理します。これにより、共有メモリ内の異なるキーまたはファイルシステム内のファイルをすべて管理していると仮定して、複数のオブジェクトを使用して共有リソースを管理できるようになります。たとえば、あるアプリケーションに独自の一意のキーまたはディレクトリを割り当てて、共有リソースを変更しようとしている別のアプリケーションと競合しないようにすることができます。
Edit2:オブジェクトを作成するときにスコープを init 関数に渡すと、スコープごとに scope.cfc という名前の単一のコンポーネントを使用できることがわかりました。現在、きめの細かい名前付きロックを使用しています。改善できるかどうか教えてください。実際に修正されたコードは次のようになります (read、delete、clear のコードは除外しました)。また、scope.cfc コンポーネントのインスタンスを 1 つだけ持つ必要もなくなったようです。
<cfcomponent>
<cfscript>
variables.scope=false;
variables.scopeName=false;
</cfscript>
<cffunction name="init" access="public" output="no" returntype="scope">
<cfargument name="scope" type="struct" required="yes">
<cfargument name="scopeName" type="string" required="yes">
<cfscript>
variables.scope=arguments.scope;
variables.scopeName=arguments.scopeName;
return this;
</cfscript>
</cffunction>
<cffunction name="write" access="public" output="no" returntype="boolean">
<cfargument name="key" type="string" required="yes">
<cfargument name="value" type="any" requires="yes">
<cfargument name="timeout" type="numeric" required="no" default="10">
<cftry>
<cflock type="exclusive" name="zcore-#variables.scopeName#-scope-#arguments.key#" timeout="#arguments.timeout#" throwontimeout="yes">
<cfscript>
variables.scope[arguments.key]=arguments.value;
</cfscript>
</cflock>
<cfcatch type="lock"><cfreturn false></cfcatch>
</cftry>
<cfreturn true>
</cffunction>
</cfcomponent>
** Edit3:** このようなコンポーネント メソッドを使用してサーバー スコープから読み取るパフォーマンスをテストしたところ、読み取り専用ロックを使用するとサーバー スコープを直接読み取る場合よりも 20 倍遅く、ロックなしで 4 倍遅くなることがわかりました。リクエストごとに数百回または数千回の追加の関数呼び出しのオーバーヘッドは遅すぎます。Railo 3.3.x で行われたテスト。
非パブリック リクエストで大きなオブジェクトを構築し、単一の共有メモリ スコープ キーを設定してから、不完全なオブジェクトをスコープに書き込もうとします。例:
<cfscript>
ts=structnew();
ts.largeObject=buildLargeObject();
server.cachedObject=ts;
</cfscript>
これにより、単一の構造体キーの更新はスレッドセーフであるため、完全なオブジェクトのみを共有メモリに書き込む場合に、アプリケーション全体でのロックを回避できます。ただし、起動時にラージ オブジェクトをビルドする場合は、そのオブジェクトが完全に作成されるまで、オブジェクトがロックされていることを確認する必要があります。
アプリケーションの速度低下を避けるために、init 関数で変数スコープの代わりに this スコープを使用して、スコープ変数を直接読み取り可能にします。