0

我需要編寫一個方法,可以異步加載和解析多個網站。 這是我的方法的簡化代碼。RanToCompletion仍然在等待時

using (HTMLParser parser = new HTMLParser(proxy)) 
{ 
    var tasks = totalSites.Select(s => 
    { 
     return new Task(async() => 
     { 
      s.Entity = await parser.GetSiteDataAsync(s.Entity).ConfigureAwait(false); 
     }); 
    }).ToArray(); 
    foreach (var task in tasks) 
     task.Start(); 
    await Task.WhenAll(tasks).ConfigureAwait(false); 
} 

HTMLParser類使用HttpClient加載網站並配置它脫手。完整的代碼也使用CancellationToken來取消,並使用SemaphoreSlim來降低並行度。

問題是,當任務開始等待分析數據時,它的狀態設置爲RanToCompletion。然後程序通過Task.WhenAll並配置HTMLParser導致OperationCanceledExceptionHttpClient中。

+0

如果你的代碼將被I/O綁定,你的方法通常不應該明確地創建新的Task對象。我認爲你現在的代碼最後是'Task's包裝其他'Task's,這不是盛大的。 –

+0

@GWigWam,對不起,我錯過了簡化我的代碼。 's'用於創建'vm'。 @Damien_The_Unbeliever我認爲你是對的:我需要創建多個任務來加載它並行的多個網站。 –

+0

@AndreyAlonzov - 但是對於I/O綁定的工作,你*不應該創建任務 - 你應該利用'async'和'await',但是唯一的你應該處理的任務是通過'async'方法返回的*。 –

回答

4

就像我在評論中所說的,你不應該明確地創建Task s。我想你需要的是沿着線的東西:

using (HTMLParser parser = new HTMLParser(proxy)) 
{ 
    var tasks = totalSites.Select(s => populateEntity(s)).ToArray(); 
    /* returned tasks are already hot */ 
    //foreach (var task in tasks) 
    // task.Start(); 
    await Task.WhenAll(tasks).ConfigureAwait(false); 
} 

,然後分別:

public async Task<WhateverSIs> populateEntity(WhateverSIs s) 
{ 
    s.Entity = await parser.GetSiteDataAsync(s.Entity).ConfigureAwait(false); 
    return s; 
} 

我們沒有創建任何Tasks明確 - 我們只是async方法,包括使用那些暴露GetSiteDataAsync

+0

謝謝!我自己也做了同樣的事情。昨天我嘗試了這種方法,但是我寫了lambda中的所有邏輯,並且取消不起作用,並迫使我明確地創建任務。 –

+0

@AndreyAlonzov - 異步lambdas的問題是他們可以很容易地生成'Func'或'Action'委託 - 而'Action'有效'async void'。如果你看看[你通過lambda的地方](https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.task(v = vs.110).aspx)你會發現它只能採用一個「Action」 - 所以你無法跟蹤你的lambda的進度。 –

相關問題