Как использовать AppDomain для ограничения области действия статического класса для поточно-ориентированного использования?
-
08-07-2019 - |
Вопрос
Меня укусило плохо спроектированное решение. Это не потокобезопасно! Р>
У меня есть несколько общих классов и членов в решении, и во время разработки все было круто ...
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, но я чувствую, что это может быть "просто" ответ. Р>
Если у кого-то есть другие мысли, пожалуйста, добавьте.
Спасибо,
Кит
Просто для полноты.
Я обнаружил, что, если я пометил адаптер отправки как " Заказанная доставка " в " Дополнительные параметры транспорта " диалог я смог избежать у меня были проблемы с многопоточностью.
Я полагаю, что это еще один возможный ответ на мою проблему, но не обязательно на вопрос.
Решение
Используя домены приложения, вы можете сделать что-то вроде этого:
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);
}
}
Другие советы
Что именно является проблемой с точки зрения безопасности потоков? Я не вижу ни статического состояния, ни синглетов - и, похоже, уместно "новое" объекты ... я слепой?
Итак, какой симптом вы видите ...
Ответ AppDomain будет (относительно) медленным. Как часть системы, поддерживаемой промежуточным программным обеспечением, это может быть нормально (то есть, «относительно» находится в том же «шаровом парке»).
Если у вас действительно есть какое-то статическое состояние, другой вариант, который иногда срабатывает, - [ThreadStatic], который среда выполнения интерпретирует как " это статическое поле уникально для каждого потока " ;. Вы должны быть осторожны с инициализацией - статический конструктор в потоке A может назначить поле, но тогда поток B увидит ноль / 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]. Это сделает их локальными для каждого потока. Это может решить вашу проблему в краткосрочной перспективе. Правильным решением было бы просто перестроить ваши вещи так, чтобы они были поточно-ориентированными.
Просто для полноты. Р>
Я обнаружил, что, если я пометил адаптер отправки как " Заказанная доставка " в разделе "Дополнительные параметры транспорта" мне удалось избежать многопоточных проблем.
Я полагаю, что это еще один возможный ответ на мою проблему, но не обязательно на вопрос.
Создание и разрушение домена приложения для каждого вызова - я так понимаю, вы не беспокоитесь о производительности на этом вызове?
В идеале вы должны изменить вызываемый код, чтобы он был потокобезопасным.