Обмен данными между AppDomains
У меня есть процесс, который может иметь несколько AppDomains. Каждый AppDomain собирает некоторую статистику. Через определенное время я хочу скопировать эти статистические данные и сохранить их в файл.
Один из способов сделать это - Remoting, которого я хочу избежать.
Единственный другой метод, который я имею в виду, заключается в том, чтобы сохранить каждую информацию AppDomain в файле, и через определенное время один из AppDomain собирает все данные и накапливает их.
Но было бы идеально, если бы все это можно было сделать в памяти, без сериализации информации, передаваемой между AppDomains. У кого-нибудь есть идеи?
Ответы
Ответ 1
Единственный способ избежать сериализации - представлять ваши данные с использованием объектов, которые происходят из MarshalByRefObject, но в этом случае вы по-прежнему будете иметь затраты на маршаллинг по границам AppDomain. Это может также включать рефакторинг/переписывание большей части вашего кода.
Предполагая, что сортировка по ссылке не является вариантом, вам придется сериализоваться в какой-то момент. Его просто невозможно избежать. Один из способов сделать это - это, по словам Нила Барнуэлла, с базой данных, другой будет с локальным файлом, как вы сами предлагаете.
Другим способом, который может или не может быть осуществимым в зависимости от сроков доставки и/или внедрения .NET 4.0, было бы использование файла с отображением памяти, см. .NET Framework 4.0: использование файлов с отображением памяти.
Ответ 2
Возможно обмен данными между AppDomains без затрат на Marshalling. Но это довольно хакерский путь. Вы можете создать объект исходных данных, который разделяется ссылкой между всеми AppDomains. Таким образом, вы получаете все данные в один общий объект без затрат на Marshalling. Звучит слишком легко, чтобы быть правдой?
Первое, что нужно знать, как делиться данными между AppDomains без Marshalling. Для этого вы получаете адрес объекта вашего объекта источника данных через Marshal.UnsafeAddrOfPinnedArrayElement. Затем вы передаете этот IntPtr всем AppDomains, которые заинтересованы в этом. В целевом AppDomain вам нужно передать этот IntPtr обратно в ссылку на объект, которую можно выполнить JIT:: CastAny, которая выполняется, если вы возвращаете объект из метода и нажимаете указатель на него в стек.
Виола вы поделили объект как простой указатель между AppDomains и получите InvalidCastExceptions. Проблема в том, что вы должны установить для всего вашего AppDomains LoaderOptimization.MultiDomain, чтобы убедиться, что сборка, которая определяет общий тип данных, загружается как нейтральный тип AppDomain, который имеет тот же указатель таблицы методов между всеми AppDomains.
Вы можете найти пример приложения, которое делает именно это как часть WMemoryProfiler. Подробнее см. Ссылку подробное объяснение и ссылку для скачивания к образцу кода.
Основной код
[LoaderOptimization(LoaderOptimization.MultiDomain)]
static public void Main(string[] args)
{
// To load our assembly appdomain neutral we need to use MultiDomain on our hosting and child domain
// If not we would get different Method tables for the same types which would result in InvalidCastExceptions
// for the same type.
var other = AppDomain.CreateDomain("Test"+i.ToString(), AppDomain.CurrentDomain.Evidence, new AppDomainSetup
{
LoaderOptimization = LoaderOptimization.MultiDomain,
});
// Create gate object in other appdomain
DomainGate gate = (DomainGate)other.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(DomainGate).FullName);
// now lets create some data
CrossDomainData data = new CrossDomainData();
data.Input = Enumerable.Range(0, 10).ToList();
// process it in other AppDomain
DomainGate.Send(gate, data);
// Display result calculated in other AppDomain
Console.WriteLine("Calculation in other AppDomain got: {0}", data.Aggregate);
}
}
Ответ 3
Я обычно говорю, что просто использую удаленный доступ. Для записи данных в файл требуется также сериализация. Кажется, что сериализация практически неизбежна в тех технологиях, которые вы используете. Вы должны перенести данные из одного домена приложения в другой с помощью некоторого канала, и вам придется сериализовать данные, чтобы получить их через канал.
Единственный способ избежать сериализации, по-видимому, заключается в использовании разделяемой памяти, так что оба домена приложения могут получить доступ к данным без прохождения через канал. Даже глубокое клонирование данных из одной памяти домена приложения в другую память является не более чем бинарной сериализацией (где результат не обязательно сохраняется в последовательных ячейках памяти).
Ответ 4
Я действительно ценю, что вы хотите сохранить это в памяти, но первым предложением было бы записать данные в базу данных и запрос оттуда. Remoting по-прежнему является удаленным вызовом, из-за которого возникает большая часть "стоимости" использования сервера базы данных, и вам нужно будет построить обработку транзакций, чтобы вы не потеряли данные. Если вы пишете в базу данных SQL Server, у вас есть поддержка транзакций, готовая и ожидающая вас, и она быстро быстрая для запросов.