C# 运行 解析网站的多个任务和 return 当它们全部完成时

C# Run multiple tasks that parse website and return when they all finish

我的objective是同时加载多个link并为每个

创建一个任务

该任务将调用一个异步方法来解析 links 和 returns 子 links,在 return 中将被解析(使用 WebBrowser ) 然后他们 return 下载 link.

第一个异步方法将调用 2 个后续方法来完成该工作。

我的问题是 Task.Factory.ContinueWhenAll 仅当所有第一个方法完成时才会 return,并且不会等待其余工作完成。我只想在所有下载 link 准备就绪后继续,这可能需要多个网页解析才能完成。

目前我的代码如下:

var tasks = new List<Task>();
for (var index = 0; index < items_checklist.CheckedItems.Count; index++)
{
    var item = items_checklist.CheckedItems[index];
    Task task = Task.Factory.StartNew(
                    () => GetMirrors(((Item) item).Value, ((Item) item).Text)
                    , CancellationToken.None
                    , TaskCreationOptions.None
                    , TaskScheduler.FromCurrentSynchronizationContext()
    );
    tasks.Add(task);
 }

Task.Factory.ContinueWhenAll(tasks.ToArray(), GetLinks_Finished =>
{
    SetLinksButtonText(@"Links Ready");
    SetLinksButtonState(false);
    SetDownloadButtonState(true);
    Cursor.Current = DefaultCursor;
});

这将 return 当所有 GetMirrors 完成时,但 GetMirrors 会调用 "tempbrowser_DocumentCompleted"(WebBrowser 完成事件),后者又会调用 "LoadLinkIntoQueue" 将下载 link 加载到排队。

我希望 ContinueWhenAll 在执行完所有 LoadLinkIntoQueue 后恢复。

我缺少什么逻辑?

您可以在 GetMirrors 方法中创建一个 TaskCompletionSource,这是在要处理的 url 循环中 Task.Factory.StartNew 调用中使用的方法。

GetMirrors 中,您将连接新 WebBrowser 的 DocumentCompleted 事件,该事件将调用 TaskCompletionSource 上的 SetResult 导致任务转换为已完成。

你的实现应该是这样的:

Task<string> GetMirrors(string url, string somethingelse )
{

    // this will signal that the Task is completed
    // we want the parent to wait
    var tcs = new TaskCompletionSource<string>(TaskCreationOptions.AttachedToParent);

    // give each task their own WebBrowser instance
    WebBrowser tempbrowser = new WebBrowser();
    tempbrowser.ScriptErrorsSuppressed = true;
    this.Controls.Add(tempbrowser);

    tempbrowser.DocumentCompleted += (s, e) => {
        // LoadLinkIntoQueue call 
        // we have a result so signal to the CompletionSource that we're done
        tcs.SetResult(e.Url.ToString());

        this.Controls.Remove(tempbrowser);
    };

    // hook up errorhandling if you need that, left as an exercise.

    tempbrowser.Navigate(url);
    // we return the Task from the completion source
    return tcs.Task ; 
}

如果您想 return 发生异常,也可以在 TaskCompletionSource 实例上调用 SetException。

请注意,在这段代码中,我为每个任务实例化了一个 WebBrowser,因此您不必担心将任务序列化为仅让一个 WebBrowser 控件处理一个任务。