Skip to main content
 首页 » 编程设计

c#之第一次使用 Parallel.ForEach UI 仍然锁定

2024年02月04日30zlslch

我在这里尝试做的是循环一个充满 URL 的 ListView ,检查源代码是否包含字符串,如果包含则用 YES 或 NO< 更新 UI ListView .

我忘记了 Parallel.ForEach 方法,所以决定尝试一下(我什至不确定这是否是最佳解决方案)

Parallel.ForEach(listViewMain.Items.Cast<ListViewItem>(), row => 
{ 
    try 
    { 
 
        string html = Helpers.GetRequest(row.Text); 
 
        if (html.Contains(txtBoxFind.Text)) 
        { 
            row.SubItems[3].Text = "YES"; 
        } 
        else 
        { 
            row.SubItems[3].Text = "NO"; 
        } 
 
    } catch(Exception) { 
 
    } 
}); 

在没有 Parallel.ForEach 的情况下,这个过程相当简单,但是 UI 仍然锁定,我实现了吗? Helpers.GetRequest 只是返回要检查的原始 HTML,我认为使用 Parallel.ForEach 会在处理时停止 UI 锁定,或者我弄错了,任何帮助都是赞赏。

请您参考如下方法:

在我们开始之前,请注意 Parallel.ForEach 本身是一个阻塞调用,因此这就是您遇到 UI 没有响应的原因。

好的读物是 Avoid Executing Parallel Loops on the UI Thread 来自 MS 文档:

... the parallel loop blocks the UI thread on which it’s executing until all iterations are complete.

也就是说,如果不处理 CPU 绑定(bind)工作而是处理 I/O 绑定(bind)工作,您应该使用基于 Task 的方法。由于您正在处理网络调用,因此您应该坚持执行任务。试试这个:

    public async Task DoSomething() 
    { 
        // Process the items parallel 
        await Task.WhenAll(listViewMain.Items.Cast<ListViewItem>().Select(async row => 
        { 
            // wrap the long running call in a async Task 
            string html = await Task.Run(() => Helpers.GetRequest(row.Text)); 
 
            // no need for context capturing and invokes, this is running on the UI thread 
            var containsText = html.Contains(txtBoxFind.Text); 
            if (containsText) 
            { 
                row.SubItems[3].Text = "YES"; 
            } 
            else 
            { 
                row.SubItems[3].Text = "NO"; 
            } 
        })); 
    } 

如果您可以使 Helpers.GetRequest(row.Text) 成为基于任务的方法,那就更好了,您可以这样做:

    public async Task DoSomething() 
    { 
        // Process the items parallel 
        await Task.WhenAll(listViewMain.Items.Cast<ListViewItem>().Select(async row => 
        { 
            // wrap the long running call in a async Task 
            string html = await Helpers.GetRequestAsync(row.Text); 
 
            // no need for context capturing and invokes, this is running on the UI thread 
            var containsText = html.Contains(txtBoxFind.Text); 
            if (containsText) 
            { 
                row.SubItems[3].Text = "YES"; 
            } 
            else 
            { 
                row.SubItems[3].Text = "NO"; 
            } 
        })); 
    } 

但我们需要查看 Helpers.GetRequest(row.Text) 的代码来帮助您。

编辑

您已经展示了 GetRequest 的代码。 WebClient 不是基于任务的,试试 HttpClient:

public async Task<string> GetRequestAsync(string url) 
{ 
    var html = ""; 
    using (HttpClient wc = new HttpClient()) 
    { 
        html = await wc.GetStringAsync(url); 
    } 
    return html; 
}