如果Task的回调获得对实例本身的引用,是否将对该实例进行垃圾回收? [英] Will an instance be garbage collected if a Task's callback get reference to the instance itself?

查看:118
本文介绍了如果Task的回调获得对实例本身的引用,是否将对该实例进行垃圾回收?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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.但是,侧面Func中有一个Task.Run(). Task的回调函数引用实例的Obj属性.在这种情况下,我仍可以由GC收集吗?

Please consider the above codes and neglect if it makes sense first. In the above case, I did not store instance of Cls into any variable, seems that nothing is referencing that instance and it would be GC. However, there is a Task.Run() in side Func. The callback function of the Task make a reference to the instance's Obj property. May I will it still be collected by GC in such case?

我问这个问题是因为在Microsoft 文档

I am asking this question because in Microsoft doc of SignalR it stated

在调用依赖于中心保持活动状态的异步方法时,请使用await.

Use await when calling asynchronous methods that depend on the hub staying alive.

我不明白,只要Clients.All.SendAsync中的某些内容引用Hub本身,Hub为何仍无法存活...

I don't understand how come the Hub will not still alive as long as something inside Clients.All.SendAsync is referencing to the Hub itself...

谢谢.

推荐答案

不,不会被垃圾回收,因为您已经引用了Task.Run()中的this(即使您在调用Thead.Sleep()).

No, it will not be garbage collected, because you have reference to this in Task.Run() (even if you reference this before call to Thead.Sleep()).

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

However, if you're running this code in Azure Functions, for example, the framework may terminate your app instance and the code in callback will not ever run (not because of being garbage collected).

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

By the way, you can manually check if it is garbage collected by calling GC.Collect(), which performs garbage collection.

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

You can use this code to test it (run it in C# Interactive, for example)

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);

如果您未在Task.Run(..)中引用this.Obj,则它将输出以下内容:

If you don't reference this.Obj in Task.Run(..), then it will output this:

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

但是,如果这样做,它将输出以下内容:

but if you do, it will output this:

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

这篇关于如果Task的回调获得对实例本身的引用,是否将对该实例进行垃圾回收?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆