Task.Run() 访问方法范围变量是否安全?

Is it safe for Task.Run() to access Method scope variables?

以下是否保证打印“成功”,或者垃圾收集是否可能吃掉“dummyValue”对象,因为 TaskTest() 在它 returns 可以完成任务之前很久就结束了?

public class DummyValueClass
{
    public string Value { get; private set; }

    public DummyValueClass(string value)
    {
        Value = value;
    }
}

public class ScopeTest
{
    public Task<string> TaskTest()
    {
        var dummyValue = new DummyValueClass("Success");

        return Task.Run(() =>
        {
            Thread.Sleep(10000);
            return dummyValue?.Value;
        });
    }
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Beginning Test");
        var scopeTester = new ScopeTest();
        var task = scopeTester.TaskTest();
        Console.WriteLine(task.Result);
    }
}

是的,保证打印“成功”。因为当 lambda 捕获其范围外的值时,编译器将生成一个对象,该对象“捕获”从其范围外获取的变量。所以它不会被垃圾收集

这称为 闭包

var dummyValue = new DummyValueClass("Success");

return Task.Run(() =>
{
    Thread.Sleep(10000);
    return dummyValue?.Value;
});

简而言之,编译器创建了一个编译器生成class并捕获dummyValue作为实例成员

清理后的代码示例。您可以查看所有详细信息 here

public class ScopeTest
{
   [CompilerGenerated]
   private sealed class GeneratedClass
   {
      public DummyValueClass dummyValue;

      internal string InternalTaskTest()
      {
         Thread.Sleep(10000);
         DummyValueClass dummyValueClass = dummyValue;
         if (dummyValueClass == null)
         {
            return null;
         }
         return dummyValueClass.Value;
      }
   }

   public Task<string> TaskTest()
   {
      GeneratedClass generatedClass = new GeneratedClass();
      generatedClass.dummyValue = new DummyValueClass("Success");
      return Task.Run(new Func<string>(generatedClass.InternalTaskTest));
   }
}

任务现在有一个指向生成的方法的方法指针,它又是GeneratedClass的根,它有一个参考你的DummyValueClass。结果是 垃圾收集器 知道你的 reference 还活着。