AppDomainを使用して、スレッドセーフで使用する静的クラスのスコープを制限する方法は?
-
08-07-2019 - |
質問
設計が不十分なソリューションに悩まされました。スレッドセーフではありません!
ソリューションにはいくつかの共有クラスとメンバーがあり、開発中はすべてクールでした...
BizTalkは私の戦艦を沈めました。
カスタムBizTalkアダプターを使用してアセンブリを呼び出しています。アダプターはコードを呼び出して並列に実行しているため、すべて同じAppDomainで複数のスレッドを使用していると想定しています。
私がやりたいのは、コードを独自のAppDomainの下で実行することです。これにより、私が抱えている共通の問題が互いに干渉することはありません。
BizTalkアダプターがインスタンス化してからProcess()メソッドを実行する非常に単純なクラスがあります。
Process()メソッド内に新しいAppDomainを作成したいので、BizTalkが別のスレッドをスピンするたびに、独自のバージョンの静的クラスとメソッドが作成されます。
BizTalkAdapterコード:
// this is inside the BizTalkAdapter and it is calling the Loader class //
private void SendMessage(IBaseMessage message, TransactionalTransmitProperties properties)
{
Stream strm = message.BodyPart.GetOriginalDataStream();
string connectionString = properties.ConnectionString;
string msgFileName = message.Context.Read("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties") as string;
Loader loader = new Loader(strm, msgFileName, connectionString);
loader.Process();
EventLog.WriteEntry("Loader", "Successfully processed: " + msgFileName);
}
これはBizTalk呼び出しクラスです:
public class Loader
{
private string connectionString;
private string fileName;
private Stream stream;
private DataFile dataFile;
public Loader(Stream stream, string fileName, string connectionString)
{
this.connectionString = connectionString;
this.fileName = fileName;
this.stream = stream;
}
public void Process()
{
//***** Create AppDomain HERE *****
// run following code entirely under that domain
dataFile = new DataFile(aredStream, fileName, connectionString);
dataFile.ParseFile();
dataFile.Save();
// get rid of the AppDomain here...
}
}
FYI:LoaderクラスはdataFileクラスとは別のDLLにあります。
ご協力いただければ幸いです。私はコードをスレッドセーフにする作業を続けますが、これは「シンプル」になり得ると感じています。回答。
他に考えている人がいれば、投げてください。
ありがとうございます、
キース
完全を期すため。
送信アダプターを「注文配達」としてマークすると、に 「トランスポートの詳細オプション」ダイアログを回避することができました 私が抱えていたマルチスレッドの問題。
これは私の問題に対する別の可能な答えであると考えていますが、そうではありません 必ず質問に。
解決
アプリドメインを使用すると、次のようなことができます:
public class Loader
{
private string connectionString;
private string fileName;
private Stream stream;
private DataFile dataFile;
public Loader(Stream stream, string fileName, string connectionString)
{
this.connectionString = connectionString;
this.fileName = fileName;
this.stream = stream;
}
public void Process()
{
//***** Create AppDomain HERE *****
string threadID = Thread.CurrentThread.ManagedThreadId.ToString();
AppDomain appDomain = AppDomain.CreateDomain(threadID);
DataFile dataFile =
(DataFile) appDomain.CreateInstanceAndUnwrap(
"<DataFile AssemblyName>",
"DataFile",
true,
BindingFlags.Default,
null,
new object[]
{
aredstream,
filename,
connectionString
},
null,
null,
null);
dataFile.ParseFile();
dataFile.Save();
appDomain.Unload(threadID);
}
}
他のヒント
スレッドの安全性の面で、どの部分がまさに苦痛ですか?私は静的な状態もシングルトンも見ることができません-そして、適切な&quot; new&quot;があるようです。オブジェクト...私は盲目ですか?
だからあなたが見ている症状は何ですか...
AppDomainの回答は(比較的)遅くなります。ミドルウェアを使用したシステムの一部として、これは問題ないかもしれません(つまり、「比較的」が同じボールパークにあります)。
どこかに何らかの静的状態がある場合、動作する可能性のある別のオプションは[ThreadStatic]です。これは、ランタイムが「この静的フィールドはスレッドごとに一意である」と解釈します。ただし、初期化には注意する必要があります-スレッドAの静的コンストラクターはフィールドを割り当てるかもしれませんが、スレッドBはnull / 0 / etcを参照します。
シーケンシャルに実行したいコードをロックするだけではどうですか? これはボトルネックになりますが、マルチスレッド環境で動作するはずです。
public class Loader
{
private static object SyncRoot = new object();
private string connectionString;
private string fileName;
private Stream stream;
private DataFile dataFile;
public Loader(Stream stream, string fileName, string connectionString)
{
this.connectionString = connectionString;
this.fileName = fileName;
this.stream = stream;
}
public void Process()
{
lock(SyncRoot) {
dataFile = new DataFile(aredStream, fileName, connectionString);
dataFile.ParseFile();
dataFile.Save();
}
}
}
互いに競合する静的な共有を持っている場合は、それらに[ThreadStatic]属性を追加してみてください。これにより、各スレッドに対してローカルになります。それは短期的にあなたの問題を解決するかもしれません。正しい解決策は、単にあなたのデータをスレッドセーフに再設計することです。
完全を期すために。
送信アダプターを「注文配達」としてマークすると、 &quot; Transport Advanced Options&quot;ダイアログ私が抱えていたマルチスレッドの問題を回避することができました。
これは私の問題に対する別の可能な答えですが、必ずしも質問に対する答えではないようです。
各呼び出しでappdomainを作成および破棄します。この呼び出しのパフォーマンスについて心配していませんか?
理想的には、呼び出されたコードをスレッドセーフに変更する必要があります。