2016-07-22 22 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" }; 
}