Как использовать AppDomain, чтобы ограничить область действия статического класса для поточно-ориентированного использования? - PullRequest
7 голосов
/ 21 ноября 2008

Меня укусило плохо спроектированное решение. Это не потокобезопасно!

У меня есть несколько общих классов и членов в решении, и во время разработки все было круто ...
BizTalk потопил мой боевой корабль.

Мы используем пользовательский адаптер BizTalk для вызова моих сборок. Адаптер вызывает мой код и работает параллельно, поэтому я предполагаю, что он использует несколько потоков в одном домене приложений.

Я хотел бы, чтобы мой код работал под собственным доменом приложений, чтобы общие проблемы, которые у меня есть, не мешали друг другу.

У меня есть очень простой класс, который реализует адаптер BizTalk, а затем запускает метод Process ().

Я хотел бы создать новый AppDomain внутри моего метода Process (), поэтому каждый раз, когда 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 Calls:

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 находится в отдельной DLL от класса dataFile.

Любая помощь будет принята с благодарностью. Я буду продолжать работать над созданием кода Thread-Safe, но я чувствую, что это может быть «простым» ответом.

Если у кого-то есть другие мысли, пожалуйста, добавьте.

Спасибо,
Кит

Только для полноты.

Я обнаружил, что если я пометил отправляющий адаптер как "Заказанная доставка" в диалоговое окно «Дополнительные параметры транспорта» мне удалось избежать у меня были проблемы с многопоточностью.

Полагаю, это еще один возможный ответ на мою проблему, но не обязательно к вопросу.

Ответы [ 6 ]

3 голосов
/ 21 ноября 2008

Какой бит, в действительности, является проблемой с точки зрения безопасности потока? Я не вижу ни статического состояния, ни синглетонов - и, кажется, есть подходящие "новые" объекты ... я слепой?

Итак, какой симптом вы видите ...

Ответ AppDomain будет (относительно) медленным. Как часть системы с промежуточным программным обеспечением, это может быть нормально (т. Е. «Относительно» находится в том же «шаровом парке»).

Если у вас есть какое-то статическое состояние где-то, другой вариант, который иногда работает, - [ThreadStatic], который среда выполнения интерпретирует как "это статическое поле уникально для потока". Вы должны быть осторожны с инициализацией - статический конструктор в потоке A может назначить поле, но тогда поток B увидит ноль / 0 / и т. Д.

3 голосов
/ 21 ноября 2008

Используя домены приложения, вы можете сделать что-то вроде этого:

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);       
    }
}
0 голосов
/ 28 ноября 2008

Создание и разрушение домена приложения для каждого вызова - я так понимаю, вы не беспокоитесь о производительности на этом вызове?

В идеале вы должны изменить вызываемый код для обеспечения безопасности потоков.

0 голосов
/ 22 ноября 2008

Просто для полноты.

Я обнаружил, что если я пометил отправляющий адаптер как «Заказанная доставка» в диалоговом окне «Дополнительные параметры транспорта», я смог избежать многопоточных проблем, которые у меня были.

Я полагаю, что это еще один возможный ответ на мою проблему, но не обязательно на вопрос.

0 голосов
/ 21 ноября 2008

Если у вас есть общая статика, которая конфликтует друг с другом, вы можете попробовать добавить к ним атрибут [ThreadStatic]. Это сделает их локальными для каждого потока. Это может решить вашу проблему в краткосрочной перспективе. Правильным решением было бы просто перестроить ваши вещи так, чтобы они были поточно-ориентированными.

0 голосов
/ 21 ноября 2008

Почему бы просто не поставить блокировку вокруг кода, который вы хотите выполнить последовательно? Это будет узким местом, но оно должно работать в многопоточной среде.

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

    }

}
...