为什么这个Parallel.ForEach代码会冻结程序?

dsp*_*099 17 c# user-interface multithreading visual-studio-2010 task-parallel-library

更多新手问题:

这段代码从主窗口的列表中抓取了许多代理(我无法弄清楚如何在不同的函数之间使变量可用)并检查每个代理(简单的httpwebrequest),然后将它们添加到名为的列表中finishedProxies.

出于某种原因,当我按下开始按钮时,整个程序挂起.我的印象是Parallel为每个动作创建单独的线程,只留下UI线程,以便它响应?

private void start_Click(object sender, RoutedEventArgs e)
        {
            // Populate a list of proxies
            List<string> proxies = new List<string>();
            List<string> finishedProxies = new List<string>();

            foreach (string proxy in proxiesList.Items)
            {
                proxies.Add(proxy);
            }

            Parallel.ForEach<string>(proxies, (i) =>
            {
                string checkResult;
                checkResult = checkProxy(i);

                finishedProxies.Add(checkResult);
                // update ui
                /*
                 status.Dispatcher.Invoke(
                  System.Windows.Threading.DispatcherPriority.Normal,
                  new Action(
                    delegate()
                    {
                        status.Content = "hello" + checkResult;
                    }
                )); */
                // update ui finished


                //Console.WriteLine("[{0}] F({1}) = {2}", Thread.CurrentThread.Name, i, CalculateFibonacciNumber(i));
            });


        }
Run Code Online (Sandbox Code Playgroud)

我已经尝试使用已注释掉的代码来更改Parallel.Foreach中的UI,它会在按下启动按钮后冻结程序.它之前对我有用,但我使用的是Thread类.

如何从Parallel.Foreach内部更新UI,如何使Parallel.Foreach工作,以便它在工作时不会使UI冻结?

这是整个代码.

Jon*_*Jon 19

您不能在UI线程中启动并行处理.请参阅此页面中 "避免在UI线程上执行并行循环"标题下的示例.

更新:或者,你可以简单地创建一个新的线程manuall并开始内部处理,就像我看到你做的那样.这也没有错.

另外,正如Jim Mischel指出的那样,您同时从多个线程访问列表,因此存在竞争条件.无论是替代ConcurrentBagList,或包裹列出了内lock声明每次访问他们的时间.

  • 在"替换`ConcurrentBag`为`List`"中你是什么意思?......你的意思是"将`List`替换为'ConcurrentBag`"吗? (4认同)
  • @Fulproof:我相信正确的英语是“substitute A *for* B” ==“substitute B *with* A”。 (2认同)
  • 乔恩,你说得对,但由于这是一个国际论坛,如果使用更直接的表达方式会更好。“要么使用 ConcurrentBag 而不是 List 要么......”对每个人来说都更容易阅读,我认为。吸取教训,思考。:) (2认同)

Ste*_*nVL 9

避免在使用Parallel语句时无法写入UI线程的问题的好方法是使用Task Factory和委托,请参阅以下代码,我使用它来迭代目录中的一系列文件,以及在并行的foreach循环中处理它们,在处理每个文件之后,UI线程被发信号通知并更新:

var files = GetFiles(directoryToScan);

tokenSource = new CancellationTokenSource();
CancellationToken ct = tokenSource.Token;

Task task = Task.Factory.StartNew(delegate
{
    // Were we already canceled?
    ct.ThrowIfCancellationRequested();

    Parallel.ForEach(files, currentFile =>
    {
        // Poll on this property if you have to do 
        // other cleanup before throwing. 
        if (ct.IsCancellationRequested)
        {
            // Clean up here, then...
            ct.ThrowIfCancellationRequested();
        }

        ProcessFile(directoryToScan, currentFile, directoryToOutput);

        // Update calling thread's UI
        BeginInvoke((Action)(() =>
        {
            WriteProgress(currentFile);
        }));
    });
}, tokenSource.Token); // Pass same token to StartNew.

task.ContinueWith((t) =>
        BeginInvoke((Action)(() =>
        {
            SignalCompletion(sw);
        }))
);
Run Code Online (Sandbox Code Playgroud)

以及实际UI更改的方法:

void WriteProgress(string fileName)
{
    progressBar.Visible = true;
    lblResizeProgressAmount.Visible = true;
    lblResizeProgress.Visible = true;

    progressBar.Value += 1;
    Interlocked.Increment(ref counter);
    lblResizeProgressAmount.Text = counter.ToString();

    ListViewItem lvi = new ListViewItem(fileName);
    listView1.Items.Add(lvi);
    listView1.FullRowSelect = true;
}

private void SignalCompletion(Stopwatch sw)
{
    sw.Stop();

    if (tokenSource.IsCancellationRequested)
    {
        InitializeFields();
        lblFinished.Visible = true;
        lblFinished.Text = String.Format("Processing was cancelled after {0}", sw.Elapsed.ToString());
    }
    else
    {
        lblFinished.Visible = true;
        if (counter > 0)
        {
            lblFinished.Text = String.Format("Resized {0} images in {1}", counter, sw.Elapsed.ToString());
        }
        else
        {
            lblFinished.Text = "Nothing to resize";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!