7

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

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

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

如果有人有其他想法,请提出。

谢谢你,
基思

只是为了完整性。

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

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

4

6 回答 6

3

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

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);       
    }
}
于 2008-11-21T04:15:03.860 回答
3

就线程安全而言,究竟哪一点是痛苦的?我看不到任何静态或单例——而且似乎有合适的“新”对象……我是瞎了吗?

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

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

如果您在某处确实有一些静态状态,则有时有效的另一个选项是 [ThreadStatic] - 运行时将其解释为“此静态字段对每个线程都是唯一的”。不过,您需要小心初始化 - 线程 A 上的静态构造函数可能会分配一个字段,但随后线程 B 会看到 null/0/等。

于 2008-11-21T05:28:11.140 回答
0

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

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();
        }

    }

}
于 2008-11-20T23:41:44.367 回答
0

如果您有相互冲突的共享静态,那么您可能想尝试向它们添加 [ThreadStatic] 属性。这将使它们成为每个线程的本地。这可能会在短期内解决您的问题。一个正确的解决方案是简单地将你的东西重新架构为线程安全的。

于 2008-11-21T17:16:18.433 回答
0

只是为了完整性。

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

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

于 2008-11-22T00:31:59.367 回答
0

为每个呼叫创建和拆除一个 appdomain - 我认为您不担心这个的性能吗?

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

于 2008-11-28T11:52:21.473 回答