Подтвердить что ты не робот

Передавать и исполнять делегат в отдельном AppDomain

Я хочу исключить часть кода в отдельном AppDomain с делегатом. Как я могу это сделать?

UPD1: некоторые подробности о моей проблеме Моя программа обрабатывает некоторые данные (одна итерация: получение некоторых данных из БД, их оценка и создание сборок во время выполнения, выполнение динамических сборок и запись результатов в БД).

Текущее решение: каждая итерация выполняется в отдельном потоке. Лучшее решение: каждая итерация выполняется в отдельном AppDomain (для выгрузки динамических asseblies).

UPD2: все, спасибо за ответы.

Я нашел один для меня в этой теме: Замена Process.Start с AppDomains

4b9b3361

Ответ 1

Хотя вы можете сделать вызов в делегат, который будет обрабатываться отдельным AppDomain, я лично всегда использовал метод CreateInstanceAndUnwrap, который создает объект в чужом домене приложения и возвращает ему прокси.

Для этого ваш объект должен наследовать от MarshalByRefObject.

Вот пример:

    public interface IRuntime
    {
        bool Run(RuntimesetupInfo setupInfo);
    }

    // The runtime class derives from MarshalByRefObject, so that a proxy can be returned
    // across an AppDomain boundary.
    public class Runtime : MarshalByRefObject, IRuntime
    {
        public bool Run(RuntimeSetupInfo setupInfo)
        {
            // your code here
        }
    }

    // Sample code follows here to create the appdomain, set startup params
    // for the appdomain, create an object in it, and execute a method
    try
    {
        // Construct and initialize settings for a second AppDomain.
        AppDomainSetup domainSetup = new AppDomainSetup()
        {
            ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
            ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
            ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
            LoaderOptimization = LoaderOptimization.MultiDomainHost
        };

        // Create the child AppDomain used for the service tool at runtime.
        childDomain = AppDomain.CreateDomain(
            "Your Child AppDomain", null, domainSetup);

        // Create an instance of the runtime in the second AppDomain. 
        // A proxy to the object is returned.
        IRuntime runtime = (IRuntime)childDomain.CreateInstanceAndUnwrap(
            typeof(Runtime).Assembly.FullName, typeof(Runtime).FullName);

        // start the runtime.  call will marshal into the child runtime appdomain
        return runtime.Run(setupInfo);
    }
    finally
    {
        // runtime has exited, finish off by unloading the runtime appdomain
        if(childDomain != null) AppDomain.Unload(childDomain);
    }

В приведенном выше примере закодировано для выполнения метода "Запуск", передаваемого в некоторой информации об установке, и завершение метода "Выполнить" определяется, чтобы указать, что весь код в дочернем домене AppDomain завершен, поэтому мы имеем наконец блок, обеспечивающий выгрузку AppDomain.

Часто вы можете быть осторожны, какие типы вы размещаете в сборках - вы можете использовать интерфейс и поместить его в отдельную сборку, которая и вызывающая программа (наш код, который устанавливает приложение, и вызывает его), и разработчик (класс Runtime) зависит от. Этот IIRC позволяет родительскому AppDomain загружать только сборку, содержащую интерфейс, в то время как дочерний домен appdomain загружает как сборку, содержащую Runtime, так и ее зависимость (сборку IRuntime). Любые пользовательские типы, которые используются интерфейсом IRuntime (например, наш класс RuntimeSetupInfo), обычно также должны быть помещены в ту же сборку, что и IRuntime. Кроме того, будьте осторожны с тем, как вы определяете эти пользовательские типы - если они являются объектами передачи данных (вероятно, это RuntimeSetupInfo), вы должны, вероятно, пометить их атрибутом [serializable] - чтобы была передана копия объекта (сериализована из родительский appdomain для ребенка). Вы хотите избежать перенаправления вызовов из одного приложения в другой, поскольку это довольно медленно. Передача DTO по значению (сериализация) означает, что доступ к значениям в DTO не требует межсетевого вызова (так как у дочернего appdomain есть собственная копия оригинала). Разумеется, это также означает, что изменения стоимости не отражаются в исходном исходном DTO родительского приложения.

Как закодировано в примере, родительский appdomain фактически загрузит как сборки IRuntime, так и Runtime, но это связано с тем, что при вызове CreateInstanceAndUnwrap я использую typeof (Runtime), чтобы получить имя сборки и полное имя типа, Вместо этого вы можете жестко задавать или извлекать эти строки из файла, что отделяет зависимость.

Также есть метод в AppDomain с именем "DoCallBack", который выглядит так, будто он позволяет вызывать делегат в чужом AppDomain. Однако тип делегата, который он принимает, имеет тип "CrossAppDomainDelegate". Определение которого:

public delegate void CrossAppDomainDelegate()

Таким образом, он не позволит вам передавать в него какие-либо данные. И, так как я никогда не использовал его, я не могу сказать вам, есть ли какие-то особые проблемы.

Кроме того, я бы рекомендовал изучить свойство LoaderOptimization. То, что вы установили для этого, может существенно повлиять на производительность, поскольку некоторые настройки этого свойства заставляют новый appdomain загружать отдельные копии всех сборок (и JIT их и т.д.), Даже если (IIRC) сборка находится в GAC ( т.е. это включает сборки CLR). Это может дать вам ужасную производительность, если вы используете большое количество сборок из вашего дочернего приложения. Например, я использовал WPF из дочерних доменов, что вызвало большие задержки запуска для моего приложения, пока я не установил более подходящую политику загрузки.

Ответ 2

Чтобы выполнить делегирование на другой AppDomain, вы можете использовать System.AppDomain.DoCallBack(). Отличный пример имеет связанная страница MSDN. Обратите внимание, что вы можете использовать только делегаты типа CrossAppDomainDelegate.

Ответ 3

Вам нужно прочитать .NET Remoting и, в частности, Удаленные объекты, поскольку это все, что вы можете пройти через AppDomains.

Длинным и коротким является то, что ваш объект либо передается по значению, либо по ссылке (через прокси).

По значению требуется, чтобы ваш объект был Serializable. Делегаты не являются сериализуемыми afaik. Это означает, что это не очень хороший путь.

По ссылке требуется, чтобы вы наследовали от MarshalByRefObject. Таким образом, удаленная инфраструктура может создать прокси-сервер. Однако это также означает, что ваш делегат будет выполнен на машине, которая его создает, а не в домене клиентского приложения.

В общем, это будет сложно. Возможно, вам стоит подумать о том, чтобы сделать ваши делегаты полноценными сериализуемыми объектами, чтобы их можно было легко перемещать с удалением (и будет хорошо работать с другими технологиями).

Ответ 4

Это не отвечает на ваш вопрос напрямую, но, возможно, было бы лучше создать службу WCF или веб-службу в другом AppDomain для сохранения изоляции. Я не знаю вашей конкретной ситуации, но изолированный архитектурный дизайн - это почти всегда правильный путь.