2013-01-11 126 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只会报告其中的一个错误。

相关问题