2013-01-11 125 views
2

我的實際程序比這更復雜,但我試圖簡化的東西。異步/等待和Task.Run - 如何知道什麼時候完成

因此,讓我們說我正在閱讀一個URL列表的文件。我想從每個URL下載HTML並處理它。處理過程可能有點複雜,所以我希望它在單獨的線程上完成。

基本問題是如何判斷何時完成所有處理。例如,如果用戶試圖在處理所有URL之前關閉該程序,我想給他一條消息而不是退出程序。或者,我希望在處理所有URL後終止程序(也許帶有MsgBox(「完成」)消息)。

我想我的代碼看起來如下(假設我有一個外循環讀取URL,並調用這個例程)...

List<Task> TaskList = new List<Task>(); 

async void ProcessSingleUrl(string url) { 
var web = new HttpClient(); 
    var WebPageContents = await web.GetStringAsync(url); 
    Task t = Task.Run(() => ProcessWebPage(WebPageContents); 
    TaskList.Add(t); 
} 

上面的代碼應該運行速度非常快(異步方法立即運行非常好),並立即返回給調用者。

但是在那個時候,我可能在TaskList中沒有任何條目,因爲在GetStringAsync完成之前沒有定義一個任務,並且當時沒有(或者只是幾個)完成。所以

Task.WaitAll(TaskList.ToArray()); 

不按我需要的方式工作。

如果絕對必要,我可以先閱讀所有網址,並知道需要多少任務,但我希望有一個更優雅的解決方案。

我想我可以在等待之前增加一個計數器,但這感覺有點遺憾。

我假設我正在構造錯誤的東西,但我不知道如何重組事情。

注意:我沒有執着於Task.Run。好的'QueueWorkItem是一種可能性,但我認爲它也有相同的問題。

回答

1

我假設我構造錯誤的東西,但我不知道如何重組事情。

我認爲這是事實。這裏是一個可能的解決方案:存儲整個計算爲Task在列表中,而不僅僅是第二部分:

async Task ProcessSingleUrlInner(string url) { 
    var web = new HttpClient(); 
    var WebPageContents = await web.GetStringAsync(url); 
    Task t = Task.Run(() => ProcessWebPage(WebPageContents); 
    await t; 
} 

void ProcessSingleUrl(string url) { 
var t = ProcessSingleUrlInner(url); 
TaskList.Add(t); 
} 

等待此列表上的所有任務將保證一切都做。可能你需要將這個想法適應你的確切需求。

+0

在我看來,這是行不通的。 –

+0

在我看來,這是行不通的。首先,你的意思是「迴歸t」而不是「等待」;? (沒有做任何事情後會「等待」什麼,對不對?)如果是這樣,我們不會回到我們開始的地方嗎?還是我錯過了一些東西(再次!)? –

+0

@LarrySmith如果你遺漏了尾部,等待'ProcessSingleUrlInner'返回的任務將會在* ProcessWebPage完成之前完成。等待它「包含」ProcessWebPage到函數中。它確保'ProcessSingleUrlInner'在'ProcessWebPage'完成之前沒有完成。這就是這個解決方案的關鍵:你得到的任務只有在* everything *完成時才能完成。那是你想要的,對吧? – usr

0

我假設你得到的網址列表爲IEnumerable<string>或其他一些。

您可以使用LINQ到每個URL轉換爲Task,然後await他們全部完成:

async Task ProcessUrls(IEnumerable<string> urls) 
{ 
    var tasks = urls.Select(async url => 
    { 
    var web = new HttpClient(); 
    var WebPageContents = await web.GetStringAsync(url); 
    await Task.Run(() => ProcessWebPage(WebPageContents); 
    }); 
    await Task.WhenAll(tasks); 
} 

請注意,如果您使用此解決方案,並有有錯誤多個不同的URL,然後Task.WhenAll只會報告其中的一個錯誤。

相關問題