Будет ли экземпляр собираться мусором, если обратный вызов Задачи получит ссылку на сам экземпляр? - PullRequest
0 голосов
/ 19 июня 2020
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 в какой-либо переменной, кажется, что ничего не ссылается на этот экземпляр, и это будет G C. Однако на стороне Func стоит Task.Run(). Функция обратного вызова Task делает ссылку на свойство экземпляра Obj. Могу ли я в таком случае собрать его G C?


Я задаю этот вопрос, потому что в Microsoft do c SignalR указано

Используйте ожидание при вызове асинхронных методов, зависящих от работоспособности концентратора.

Я не понимаю, почему Hub не работает, пока что-то внутри Clients.All.SendAsync ссылается на сам Hub ...

Спасибо.

1 Ответ

2 голосов
/ 19 июня 2020

Нет, сборщик мусора не будет, потому что у вас есть ссылка на this в Task.Run() (даже если вы ссылаетесь на this перед вызовом Thead.Sleep()).

Однако, если вы запускаете этот код в Azure функциях, например, фреймворк может завершить работу экземпляра вашего приложения, и код в обратном вызове никогда не запустится (не из-за сборки мусора).

Кстати, вы можете вручную проверить, собран ли он сборщик мусора, вызвав GC.Collect(), который выполняет сборку мусора.

Вы можете использовать этот код для тестирования (запустите его в C# Интерактивный, например)

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
...