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));
            });
        }
我已经尝试使用已注释掉的代码来更改Parallel.Foreach中的UI,它会在按下启动按钮后冻结程序.它之前对我有用,但我使用的是Thread类.
如何从Parallel.Foreach内部更新UI,如何使Parallel.Foreach工作,以便它在工作时不会使UI冻结?
Jon*_*Jon 19
您不能在UI线程中启动并行处理.请参阅此页面中 "避免在UI线程上执行并行循环"标题下的示例.
更新:或者,你可以简单地创建一个新的线程manuall并开始内部处理,就像我看到你做的那样.这也没有错.
另外,正如Jim Mischel指出的那样,您同时从多个线程访问列表,因此存在竞争条件.无论是替代ConcurrentBag的List,或包裹列出了内lock声明每次访问他们的时间.
避免在使用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);
        }))
);
以及实际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";
        }
    }
}
希望这可以帮助!