2014-12-28 24 views
1

當我真的不知道爲什麼時,我得到一個ArgumentOutOfRangeExceptionArgumentOutOfRangeException使用任務

Task[] downloadTasks = new Task[music.Count]; 
for (int i = 0; i < music.Count; i++) 
    downloadTasks[i] = Task.Factory.StartNew(() => DownloadAudio(music[i], lstQueue.Items[i])); 
Task.Factory.ContinueWhenAll(downloadTasks, (tasks) => 
{ 
    MessageBox.Show("All the downloads have completed!", 
     "Success", 
     MessageBoxButtons.OK, 
     MessageBoxIcon.Information); 
}); 

錯誤當for loop運行時i = 1,我不知道爲什麼它這樣做的時候,我確信music.Count = 1發生。

我總是試圖這種方法作爲一種替代for loop,得到了相同的異常:

int index = 0; 
foreach (MusicFile song in music) 
{ 
    downloadTasks[index] = Task.Factory.StartNew(() => DownloadAudio(song, lstQueue.Items[index])); 
    index++; 
} 

有沒有在上面的代碼中任何可能導致此?

我也不確定這是否相關,但是當我可以使用線程完成同樣的事情而沒有任何異常。只有當我嘗試執行這個例外出現的任務時。

回答

4

發生這種情況是因爲您通過了StartNew a Lambda Expression,它隱式捕獲了您的i變量。這種效果被稱爲Closure

爲了得到正確的行爲,你必須讓你的索引的本地副本:

for (int i = 0; i < music.Count; i++) 
{ 
    var currentIndex = i; 
    downloadTasks[i] = Task.Factory.StartNew(() => 
              DownloadAudio(music[currentIndex], 
              lstQueue.Items[currentIndex])); 
} 
+0

儘管這確實解決了上述問題,但現在我得到一個'InvalidOperationException'來試圖訪問'lstQueue.Items'。 – Minato

+0

這是因爲'lstQueue'可能是一個UI對象,您試圖從後臺線程訪問(並且不允許)。它包含什麼? –

+0

這是一個'ListView',它保存下載的進度百分比。我試圖同時更新每個下載的進度。但我會弄清楚如何去做我想做的事情。謝謝! – Minato

1

在這兩種情況下,你是第一個例子closing over the loop variablei,或者您手動分配index在第二。

發生了什麼事情是在循環完成後使用了最終值i/index,這是i++增加到迭代數組的大小之後。 (見also here

要麼與一個額外的變量捕捉i的值在循環內按@Yuval,或者看一下兩個集合耦合在一起,這樣你就不需要重複musiclstQueue方式獨立地,例如在這裏,我們的兩個集合預合併成一個新的匿名類:

var musicQueueTuples = music.Zip(lstQueue, (m, q) => new {Music = m, QueueItem = q}) 
    .ToList(); 

// Which now allows us to use LINQ to project the tasks: 
var downloadTasks = musicQueueTuples.Select(
     mqt => Task.Factory.StartNew(
     () => DownloadAudio(mqt.Music, mqt.QueueItem))).ToArray(); 

Task.Factory.ContinueWhenAll(downloadTasks, (tasks) => ... 
0

閉包是你的問題,其中變量i正在被黏巴達表達式引用,因此它可以訪問我,而總是直接讀取其價值來自記憶。

您可以創建一個創建任務處理程序的工廠函數。您可以按照以下想法解決問題。

private Action CreateTaskHandler(int arg1) 
{ 
    return() => DownloadAudio(music[arg1], lstQueue.Items[arg1]) 
} 

Task[] downloadTasks = new Task[music.Count]; 
for (int i = 0; i < music.Count; i++) 
    downloadTasks[i] = Task.Factory.StartNew(CreateTaskHandler(i)); 
} 
相關問題