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

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

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

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

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

Я нашел один для меня в этом потоке: Замена Процесса. Запустите с AppDomains

31
задан Community 23 May 2017 в 11:47
поделиться

3 ответа

Хотя вы можете сделать вызов делегата, который будет обрабатываться отдельным 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);
    }

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

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

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

В AppDomain также есть метод под названием 'DoCallBack', который выглядит так, как будто он позволяет вызвать делегата в иностранном AppDomain. Однако, тип делегата, который он принимает, имеет тип 'CrossAppDomainDelegate'. Определение этого типа следующее:

public delegate void CrossAppDomainDelegate()

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

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

.
49
ответ дан 27 November 2019 в 22:01
поделиться
[

] Вам нужно прочитать на [].NET Remoting[] и особенно на [] Remote Objects[], так как это все, что вы можете пройти через AppDomains.[

] [

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

] [

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

] [

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

] [

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

].
6
ответ дан 27 November 2019 в 22:01
поделиться
[

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

]
3
ответ дан 27 November 2019 в 22:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: