如何使用AppDomain限制静态类的范围以便线程安全使用?

我被一个架构不佳的解决方案所困扰。 它不是线程安全的!

我在解决方案中有几个共享类和成员,在开发过程中一切都很酷……
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... } } 

仅供参考:Loader类位于dataFile类的单独DLL中。

任何帮助,将不胜感激。 我将继续努力使代码线程安全,但我觉得这可能是“简单”的答案。

如果有人有任何其他想法,请投入。

谢谢,
基思

只是为了完整。

我确实发现,如果我在“传输高级选项”对话框中将发送适配器标记为“有序传递”,我就能避免出现我遇到的multithreading问题。

我认为这是我的问题的另一个可能的答案,但不一定是问题。

使用应用域您可以执行以下操作:

 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", true, BindingFlags.Default, null, new object[] { aredstream, filename, connectionString }, null, null, null); dataFile.ParseFile(); dataFile.Save(); appDomain.Unload(threadID); } } 

确切地说,哪一点在线程安全方面是一种痛苦? 我看不到任何静态或单身 – 而且似乎有适当的“新”物体……我是不是在失明?

那么你看到的症状是什么……

AppDomain答案将(相对)缓慢。 作为中间件支持系统的一部分,这可能没问题(即“相对”在同一个球场中)。

如果你确实在某个地方有一些静态,另一个有时可行的选项是[ThreadStatic] – 运行时将其解释为“这个静态字段对于每个线程是唯一的”。 但是你需要小心初始化 – 线程A上的静态构造函数可能会分配一个字段,但是线程B会看到null / 0 / etc。

为什么不直接锁定要按顺序执行的代码? 这将是一个瓶颈,但它应该在multithreading环境中工作。

 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]属性添加到它们中。 这将使它们成为每个线程的本地。 这可能会在短期内解决您的问题。 一个正确的解决方案是简单地重新架构你的东西是线程安全的。

只是为了完整。

我确实发现,如果我在“传输高级选项”对话框中将发送适配器标记为“有序传递”,我就能避免出现我遇到的multithreading问题。

我认为这是我的问题的另一个可能的答案,但不一定是问题。

为每个电话创建和拆除appdomain – 我认为你不担心这个电话的性能吗?

理想情况下,您应该将被调用的代码更改为线程安全。