【问题标题】:Async Operation In Destructor析构函数中的异步操作
【发布时间】:2017-09-01 15:33:14
【问题描述】:

尝试在类析构函数中运行异步操作失败。

这是代码:

public class Executor
    {
        public static void Main()
        {
            var c1 = new Class1();

            c1.DoSomething();
        }
    }

    public class Class1 
    {
        public void DoSomething()
        {

        }

        private int _i = 100;
        private int _j = 100;

        ~Class1()
        {
            Task.Run(() => _j *= 2); //Does not progress _j
            _i *= 2; //Progress _i from 100 to 200

            Thread.Sleep(1000);
            Console.WriteLine("In destructor. _i = " + _i);
            Console.WriteLine("In destructor. _j =  " + _j);
        }
    }

输出是:

In destructor. _i = 200
In destructor. _j =  100

以下Destructor page on MSDN 未提及实例销毁的线程/异步方面。

有什么想法吗?

谢谢

【问题讨论】:

  • Task.Run 将启动另一个线程,但即使您等待 1 秒也可能还不够。您需要等待任务的结果,可能使用.Wait() 来确保它完成。也就是说,我认为在析构函数中启动线程或任务是一个坏主意。应该使用析构函数来清理非托管内存。
  • 哇,在 GC 之后将引用传递给另一个线程的想法显然已经将该引用放入终结器队列中......
  • @RenéVogt 文档明确提到析构函数中的代码在完成对象之前执行。看到这个锚的底部:msdn.microsoft.com/en-us/library/66x5fx1b.aspx#Remarks
  • 我并没有说这是不可能的,但是 - 好吧 - 奇怪......在你的例子中,析构函数不是由 gc 调用的(afaik),而是因为你的程序终止了。我不确定Task.Run() 在执行/终止时是否真的能够从线程池中获取线程。
  • @RenéVogt 当然 - 我完全理解你的观点,你是对的,这个案例是指非常具体的情况,导致我提出这个问题,因为我找不到任何相关信息。感谢您的 cmets :)

标签: c# asynchronous destructor


【解决方案1】:

在您的特定示例中,无法启动新线程,因为运行时正在终止并且应用程序域正在卸载。当应用程序域卸载时 - 它将运行所有终结器并关闭所有线程。您可以通过以下方式验证这一点:

Console.WriteLine("shutdown:" + Environment.HasShutdownStarted);

在您的情况下将返回 true。如果你像这样修改你的例子:

class Program {
    static void Main(string[] args) {
        var c1 = new Class1();
        c1.DoSomething();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.ReadKey();
    }        
}

public class Class1
{
    public void DoSomething()
    {

    }

    private volatile int _i = 100;
    private volatile int _j = 100;

    ~Class1()
    {
        Console.WriteLine("shutdown:" + Environment.HasShutdownStarted);
        Task.Run(() => _j *= 2); //Does not progress _j
        //_i *= 2; //Progress _i from 100 to 200            
        Thread.Sleep(1000);
        Console.WriteLine("In destructor. _i = " + _i);
        Console.WriteLine("In destructor. _j =  " + _j);
    }
}

并在发布模式下进行优化编译 - 您会看到现在运行时没有终止,您的任务将运行良好。

显然你不应该在终结器中做这样的事情,但只是为了让你知道真正的原因。

【讨论】:

  • 您的回答符合问题,但是 - 您是否熟悉有关此问题的任何官方文档?谢谢
  • 我认为很明显,当运行时终止(因此您的进程正在退出)时,允许启动新线程并且所有正在运行的线程都被中止是没有意义的?但是我会尝试在文档中找到一些东西。
  • @SaturnTechnologies 不幸的是,我在有限的时间内无法找到有关此案例的任何具体文档。
  • 感谢您的努力和时间
猜你喜欢
  • 2018-09-29
  • 2020-10-25
  • 2023-03-16
  • 1970-01-01
  • 2020-07-18
  • 2014-07-19
  • 2017-04-14
相关资源
最近更新 更多