2016-07-22 23 views
0

我已经把一个简单的演示类,看起来像这样:在任务中维护引用?

public class HelloWorld 
    { 
     public string Name { get; set; } 
    } 

    public Main() 
    { 
     var h = new HelloWorld() { Name = "A" }; 
     Task.Factory.StartNew(() => { Console.WriteLine(h.Name); }); 
     h = new HelloWorld() { Name = "B" }; 
    } 

下面的代码打印:

这是完全合乎逻辑的,但不是我想要(我想打印A)。 我期望能够用参数调用StartNew(),该参数将保留代表内的第一个参考h,但我看不到此选项。

我错过了什么吗?

编辑:我可以看到,我可以使用

Task.Factory.StartNew(new Action<object>((obj) => { Console.WriteLine((obj as Hello).Name); }),h); 

被迫在一个类型的object传递似乎有点.NET 1.1 /预仿制药给我的,所以希望有一个更好的选择。

+0

在附注中,您不应该使用'StartNew'。改为使用'Task.Run'。 –

回答

1

您遇到的情况称为关闭并且它不是唯一的任务。每当您在lambda中使用变量时,它都会被编译器捕获到它为此构建的特殊类中。编译器生成大致是这样的:

public void Main() 
{ 
    var closure = new Main_Closure(); 
    closure.h = new HelloWorld() { Name = "A" }; 
    Task.Factory.StartNew(closure.M1); 
    closure.h = new HelloWorld() { Name = "B" }; 
} 

class Main_Closure 
{ 
    public HelloWorld h; 

    public void M1() 
    { 
     Console.WriteLine(h.Name); 
    } 
} 

而且由于closure.h可以再次分配任务开始前,你得到你所看到的结果。

在这种情况下,您可以简单地使用另一个变量来存储您的新对象。或者在调用lambda之前使用另一个变量,例如

var h1 = h; 
Task.Factory.StartNew(() => { Console.WriteLine(h1.Name); }); 
-2

你正在经历的是一个非常强大的机制,称为闭包。它在无数情况下非常有用。更深入地了解闭包的工作方式:http://csharpindepth.com/Articles/Chapter5/Closures.aspx

您的情况中的问题是h在任务有机会运行之前发生了变化。请注意,这仅仅是运气,有些时候任务可能会首先运行一些其他人可能不会运行的任务。

你可能在你的情况下做的一件事是解决这个问题,就是简单地等待任务。

var h = new HelloWorld() { Name = "A" }; 
Task.Factory.StartNew(() => { Console.WriteLine(h.Name); }).Wait(); 
h = new HelloWorld() { Name = "B" }; 

如果你是,你可以简单地等待任务的任何其他方法:

如果你的代码是一个主要的方法,你可以通过简单地在你行的末尾添加.Wait()实现这一目标用的await关键字和制作方法异步:

public async Task MyMethod() 
{ 
    var h = new HelloWorld() { Name = "A" }; 
    await Task.Factory.StartNew(() => { Console.WriteLine(h.Name); }); 
    h = new HelloWorld() { Name = "B" }; 
} 

还要记住,使用Task.Factory.StartNew是不是在大多数情况下的最佳选择。尝试倾向于使用Task.Run来代替。

+0

正如我已经评论@rinu,这完全改变了方法的语义。 –

+0

@EliArbel是对的。想象一下,如果我将'h'的声明更改为方法体的范围之外 - 您仍然无法确定在执行闭包时它将具有相同的引用,而不管是否等待。 – maxp

+0

@EliArbel,当然它会.Wait()会锁定,直到任务完成并等待创建一个延续。 – Pablo

0

在防止关闭的方法中包装任务创建。

static Task DoAsync<T>(Action<T> action, T arg) 
{ 
    return Task.Run(() => action(arg)); 
} 

static void Main(string[] args) 
{ 
    Action<HelloWorld> hello = (HelloWorld h2) => { Console.WriteLine(h2.Name); }; 

    var h = new HelloWorld() { Name = "A" }; 
    Task task = DoAsync(hello, h); 
    var h = new HelloWorld() { Name = "B" }; 
}