使用IDisposable时卸载AppDomain

问题描述 投票:0回答:2

我有一个包含工厂方法的可编组类。可以使用工厂方法在测试AppDomain中实例化该类。我试图了解我是否可以通过using(...)处置模式使用该类。

对我来说,主要关注的是测试AppDomain是否已卸载-何时。我已将静态标志添加到调用实例方法时设置的类。如果未卸载appdomain,则我相信此标志应在后续调用中保留其设置。

示例类和测试控制台应用程序看起来像这样:

using System;
using System.Reflection;
using System.Threading;

namespace AppDomainInDispose
{
    public class TestClass : MarshalByRefObject, IDisposable
    {
        public void Run()
        {
            Console.WriteLine("Hello from {0}", Thread.GetDomain().FriendlyName);
            if (_flag)
                Console.WriteLine("Flagged!");
            else
                _flag = true;
        }

        private static bool _flag = false;

        public static TestClass InstantiateInTestDomain()
        {
            var callingDomain = Thread.GetDomain();
            var setup = new AppDomainSetup() { ApplicationBase = callingDomain.SetupInformation.ApplicationBase };
            _domain = AppDomain.CreateDomain("test-domain", null, setup);
            _domain.DomainUnload += _domain_DomainUnload;

            var assembly = Assembly.GetAssembly(typeof(TestClass)).CodeBase;
            var proxy = _domain.CreateInstanceFromAndUnwrap(assembly, "AppDomainInDispose.TestClass") as TestClass;

            return proxy;
        }

        static void _domain_DomainUnload(object sender, EventArgs e)
        {
            Console.WriteLine("Unloading");
        }

        public static AppDomain _domain = null;

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~TestClass()
        {
            Dispose(false);
        }

        private void Dispose(bool disposing)
        {
            if(disposing)
            {
                //AppDomain.Unload(_domain);    // can't, as I'm in the AppDomain
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            using(var testClass = TestClass.InstantiateInTestDomain())
            {
                testClass.Run();
            }
            using (var testClass = TestClass.InstantiateInTestDomain())
            {
                testClass.Run();    // if the appdomain hasn't been unloaded then the static flag will still be set
            }

            Console.ReadKey();
        }
    }
}

似乎在此有限测试中,AppDomain正在卸载-但我不确定在哪里。有人可以解释一下AppDomain怎么回事吗?这是一个非常糟糕的主意吗?

编辑:看起来您可以有多个共享相同名称的AppDomain,但我没有意识到。另外,不会触发DomainUnload事件,因此我可能会认为该域没有被卸载。那,或者在某些情况下(例如在托管进程关闭时)未触发该事件。

c# dispose appdomain
2个回答
2
投票

嗯..这有点复杂。如果要确保在using块结束后确实卸载了应用程序域,则下面是一种这样的实现。我将“创建应用程序域和应用程序域的处理”和“正在应用程序域中加载的类型本身”这两个问题分开。 (所有这些都在主编类中)

public class GenericDisposable<T> : IDisposable
{
    public Action Dispose { get; set; }
    public T Object { get; set; }
    void IDisposable.Dispose()
    {
        Dispose();
    }

}
public static GenericDisposable<T> CreateDomainWithType<T>()
{
    var appDomain = AppDomain.CreateDomain("test-domain");
    var inst = appDomain.CreateInstanceAndUnwrap(typeof(T).Assembly.FullName, typeof(T).FullName);
    appDomain.DomainUnload += (a, b) => Console.WriteLine("Unloaded");
    return new GenericDisposable<T>() { Dispose = () => AppDomain.Unload(appDomain), Object = (T)inst };
}
public class User : MarshalByRefObject
{
    public void Sayhello()
    {
        Console.WriteLine("Hello from User");
    }
}

//Usage              
static void Main()
{
    using (var wrap = CreateDomainWithType<User>())
    {
        wrap.Object.Sayhello();
    }
    Console.Read();
}

0
投票

这是我的操作方式:

Protected Overridable Sub Dispose(disposing As Boolean)
    If Not disposedValue Then
        If disposing Then
            ' TODO: verwalteten Zustand (verwaltete Objekte) entsorgen.

            If Me.LoadContext IsNot Nothing Then
                Me.LoadContext.Unload()
                Me.LoadContext = Nothing
            End If

            If Me.Stream IsNot Nothing Then
                Me.Stream.Dispose()
                Me.Stream = Nothing
            End If

            'If Parameters.m_parameterValues IsNot Nothing Then
            '    Parameters.m_parameterValues.Clear()
            '    Parameters.m_parameterValues = Nothing
            'End If

            If Me.Domain IsNot Nothing Then
                ' https://stackoverflow.com/questions/7793074/unload-an-appdomain-while-using-idisposable
                Dim thread As System.Threading.Thread = New System.Threading.Thread(
                  Sub(domain As System.AppDomain)
                      Try
                          ' System.Threading.Thread.Sleep(1000)
                          System.AppDomain.Unload(domain)
                      Catch ex As System.Threading.ThreadAbortException
                          ' System.Console.WriteLine(ex.Message)
                          System.GC.Collect()
                          System.GC.WaitForPendingFinalizers()
                      End Try
                  End Sub
                )
                thread.IsBackground = True
                thread.Start(Me.Domain)
                Me.Domain = Nothing
            End If

            System.GC.Collect()
            System.GC.WaitForPendingFinalizers()
        End If

        ' TODO: nicht verwaltete Ressourcen (nicht verwaltete Objekte) freigeben und Finalize() weiter unten überschreiben.
        ' TODO: grosse Felder auf Null setzen.
    End If
    disposedValue = True
End Sub

秘密正在新线程中执行:

Dim thread As System.Threading.Thread = New System.Threading.Thread(
      Sub(domain As System.AppDomain)
          Try
              System.AppDomain.Unload(domain)
          Catch ex As System.Threading.ThreadAbortException
              ' System.Console.WriteLine(ex.Message)
              System.GC.Collect()
              System.GC.WaitForPendingFinalizers()
          End Try
      End Sub
)
thread.IsBackground = True
thread.Start(Me.Domain)
© www.soinside.com 2019 - 2024. All rights reserved.