首页 > 解决方案 > 如果任务的回调获取对实例本身的引用,实例是否会被垃圾收集?

问题描述

public class Cls
{
    public object Obj { get; set; }

    public async void Func()
    {
        Task.Run(() =>
        {
            Thread.Sleep(999999999);
            this.Obj = new { };
        });
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        new Cls().Func();
    }
}

请考虑上面的代码,如果它首先有意义,请忽略它。在上述情况下,我没有将实例存储Cls到任何变量中,似乎没有引用该实例,它会是 GC。但是,有一个Task.Run()在里面Func。的回调函数Task引用实例的Obj属性。在这种情况下,我还能被 GC 收集吗?


我问这个问题是因为在SignalR的 Microsoft doc中它说

在调用依赖于集线器保持活动状态的异步方法时使用等待。

我不明白Hub只要里面Clients.All.SendAsync的东西引用了Hub它自己,意志怎么会不存在……

谢谢。

标签: c#multithreadingasync-awaitgarbage-collection

解决方案


不,它不会被垃圾收集,因为您引用了thisin Task.Run()(即使您this在调用之前引用了 to Thead.Sleep())。

但是,如果您在 Azure Functions 中运行此代码,例如,框架可能会终止您的应用程序实例,并且回调中的代码将永远不会运行(不是因为被垃圾收集)。

顺便说一句,您可以通过调用GC.Collect()执行垃圾收集的方法手动检查它是否被垃圾收集。

您可以使用此代码对其进行测试(例如,在 C# Interactive 中运行它)

static async Task Main(string[] args)
{
    static void CreateObjectAndCallFunc()
    {
        var clsInstance = new Cls();
        clsInstance.Func();
    }

    CreateObjectAndCallFunc();

    Console.WriteLine($"before Task.Delay");
    await Task.Delay(10);
    Console.WriteLine($"after Task.Delay");

    Console.WriteLine($"GC.Collect()");
    GC.Collect();
    Console.WriteLine($"after GC.Collect()");

    Console.WriteLine($"before Task.Delay");
    await Task.Delay(10);
    Console.WriteLine($"after Task.Delay");
}

public class Cls
{
    public Cls() { Console.WriteLine("Cls constructor"); }
    ~Cls() { Console.WriteLine("!!! Cls deconstructor"); }

    public object Obj { get; set; }

    public void Func()
    {
        Task.Run(() =>
        {
            System.Threading.Thread.Sleep(99999);
            this.Obj = new object();
        });
    }
}
await Main(null);

如果你没有this.Obj在 Task.Run(..) 中引用,那么它会输出这个:

Cls constructor
before Task.Delay
after Task.Delay
GC.Collect()
after GC.Collect()
before Task.Delay
!!! Cls deconstructor
after Task.Delay

但如果你这样做,它会输出这个:

Cls constructor
before Task.Delay
after Task.Delay
GC.Collect()
after GC.Collect()
before Task.Delay
after Task.Delay

推荐阅读