【问题标题】:Using ParallelFor to change listview使用 ParallelFor 改变列表视图
【发布时间】:2017-03-17 19:09:10
【问题描述】:

我有一个函数可以修改我的列表视图“inputList”,列表视图有 5 列。该功能应该检查列表视图中的单元格,如果没有问题,最后一列中的单元格将具有绿色背景的“NORMAL”,如果有问题,它将在单元格中显示带有红色背景的“ERROR”在最后一列中,并更改其他列中同一行中错误单元格的颜色。

有 4k 个项目,我知道拥有这么多项目毫无意义,因为用户不会阅读它们,但我被要求这样做。

我正在使用带有 backgroundworker 的函数,但它不够快,并且认为 parallelFor 会更快。但是当我尝试使用parallelFor时,它会冻结程序。

这是带有 backgroundworker 的函数,它正在工作但它太慢了:

private void bw3_DoWork(object sender, DoWorkEventArgs e)
    {
        String tempTrain = "";
        String tempPredic = "";
        String tempNet = "";
        Boolean bTrain;
        Boolean bPredic;
        Boolean bNetErro;
        int n;
        int nNet = 0;
        String tTrain = "";
        String tPredic = "";
        int nList = 0;
        this.Invoke(new MethodInvoker(() => { 
            nList = inputList.Items.Count; 
            nNet = menuNetwork.Items.Count;
            tTrain = dtrainTextBox.Text;
            tPredic = dpredicTextBox.Text;
        }));
        for (int i = 0; i < nList; i++)
        {
            ListViewItem.ListViewSubItem temp1 = new ListViewItem.ListViewSubItem();
            temp1.BackColor = Color.LightGreen;
            temp1.Text = "NORMAL";
            this.Invoke(new MethodInvoker(() =>
            {
                inputList.Items[i].SubItems[0].BackColor = Color.White;
                inputList.Items[i].SubItems[1].BackColor = Color.White;
                inputList.Items[i].SubItems[2].BackColor = Color.White;
                tempTrain = String.Format("{0}\\{1}", tTrain, inputList.Items[i].SubItems[1].Text);
                tempPredic = String.Format("{0}\\{1}", tPredic, inputList.Items[i].SubItems[2].Text);
                tempNet = (String)inputList.Items[i].SubItems[0].Tag;
            }));
            bTrain = (!File.Exists(tempTrain));
            bPredic = (!File.Exists(tempPredic));
            bNetErro = !(int.TryParse(tempNet, out n));       
            if (!bNetErro)
            {
                if (!(n < nNet))
                {
                    bNetErro = true;
                }
            }
            this.Invoke(new MethodInvoker(delegate
            {
                if (bTrain) inputList.Items[i].SubItems[1].BackColor = Color.Red;
                if (bPredic) inputList.Items[i].SubItems[2].BackColor = Color.Red;
                if (bNetErro) inputList.Items[i].SubItems[0].BackColor = Color.Red;
                if (bTrain | bPredic | bNetErro) { temp1.Text = "Erro"; temp1.BackColor = Color.Red; }
                try { inputList.Items[i].SubItems[4] = temp1; }
                catch (ArgumentOutOfRangeException) { inputList.Items[i].SubItems.Add(temp1); }
            }));
        }
    }

这是 ParallelFor 的功能,即使我尝试一个只有项目的非常小的示例,程序也会停止工作。

 private void tent1()
    {
        ParallelOptions options = new ParallelOptions();
        options.MaxDegreeOfParallelism = 4;

        String tempTrain = "";
        String tempPredic = "";
        String tempNet = "";
        String tTrain = dtrainTextBox.Text;
        String tPredic = dpredicTextBox.Text;
        Boolean bTrain;
        Boolean bPredic;
        Boolean bNetErro;
        int n;

        int nNet = networkList.Items.Count;
        Parallel.For(0, inputList.Items.Count,
               i =>
        {
            ListViewItem.ListViewSubItem temp1 = new ListViewItem.ListViewSubItem();
            temp1.BackColor = Color.LightGreen;
            temp1.Text = "NORMAL";
            Console.WriteLine(i);
            this.Invoke(new MethodInvoker(() =>
            {
                inputList.Items[i].SubItems[0].BackColor = Color.White;
                inputList.Items[i].SubItems[1].BackColor = Color.White;
                inputList.Items[i].SubItems[2].BackColor = Color.White;
                tempTrain = String.Format("{0}\\{1}", tTrain, inputList.Items[i].SubItems[1].Text);
                tempPredic = String.Format("{0}\\{1}", tPredic , inputList.Items[i].SubItems[2].Text);
                tempNet = (String)inputList.Items[i].SubItems[0].Tag;
            }));
            bTrain = (!File.Exists(tempTrain));
            bPredic = (!File.Exists(tempPredic));
            bNetErro = !(int.TryParse(tempNet, out n));
            if (!bNetErro)
            {
                if (!(n < nNet))
                {
                    bNetErro = true;
                }
            }
            this.Invoke(new MethodInvoker(delegate
            {
                if (bTrain) inputList.Items[i].SubItems[1].BackColor = Color.Red;
                if (bPredic) inputList.Items[i].SubItems[2].BackColor = Color.Red;
                if (bNetErro) inputList.Items[i].SubItems[0].BackColor = Color.Red;
                if (bTrain | bPredic | bNetErro) { temp1.Text = "Erro"; temp1.BackColor = Color.Red; }
                try { inputList.Items[i].SubItems[4] = temp1; }
                catch (ArgumentOutOfRangeException) { inputList.Items[i].SubItems.Add(temp1); }
            }));
        });
    }

如何在不参考的情况下将列表视图的所有项目复制到列表中?我认为直接在列表视图中添加/修改,并且调用会减慢函数的速度,因此创建:List&lt;ListViewItem&gt; tList

我会修改 tList 中的所有内容,然后我会使用 inputList.Items.AddRange(tList.ToArray()); 这将删除循环内的所有调用。

    ListViewItem[] tItemsTemp = null;

this.Invoke(new MethodInvoker(() =>
            {
                tItemsTemp = new ListViewItem[inputList.Items.Count];
                inputList.Items.CopyTo(tItemsTemp, 0);
                nList = inputList.Items.Count;
                nNet = menuNetwork.Items.Count;
                tTrain = dtrainTextBox.Text;
                tPredic = dpredicTextBox.Text;
            }));
            List<ListViewItem> tList = new List<ListViewItem>(tItemsTemp);
            ListViewItem[] tItems = (ListViewItem[]) tItemsTemp.Clone();

    //Modifies the list or array of listviewitems

this.Invoke(new MethodInvoker(delegate 
    { 
        inputList.Items.Clear(); 
        // Just one of them,not the 3,just showing how i would call them.
        inputList.Items.AddRange(tItems);
        inputList.Items.AddRange(tItemsTemp);
        inputList.Items.AddRange(tList.ToArray());
    }));

但是 tItemsTemp,tItems,tList 都是引用... 如何通过创建参考进行复制?

【问题讨论】:

  • "不够快" "冻结程序" "正在运行";这些都是非常模糊的术语,你没有问任何问题。有什么问题?你得到什么?你在期待什么?你试过什么?您是否从这些帖子中搜索过类似内容并尝试过建议?
  • 这和你之前的问题有关。它看起来仍然在进行大量的 GUI 工作。为什么不试试这个:停止使用 BackgroundWorker 并分析您的代码以查看 哪个部分 是耗时的部分。现在,您只是在后台线程中投掷飞镖,希望它能加快速度。这很少奏效。我猜你最好在更新所有内容之前关闭控件的绘图。
  • 如果没有可靠地重现问题的a good, minimal, complete code example,就不可能确定问题出在哪里。但是,根据此处显示的少量代码,您似乎可能已经死锁了 GUI 线程(即通过从那里调用 Parallel.For()),或者您正在重载它(即通过大量调用在短时间内到Invoke())。无论哪种方式,请改进问题以便回答。
  • @sab669 程序停止工作,但未显示任何错误/异常。速度不够快是因为耗时太长。
  • 用更多细节编辑了问题。

标签: c# winforms listview visual-studio-2013 parallel.for


【解决方案1】:

混合 UI 和并行化并不能保证提供良好的结果。 UI 本身就是一个瓶颈,因为它运行在一个独特的线程上。更糟糕的是,您的包括一些磁盘 IO 操作(File.Exists)。

有些事情你可能还是想尝试一下:

  • 用 inputList.BeginUpdate 和 inputList.EndUpdate 封闭整个循环。这可以防止列表在您每次添加项目时刷新。

  • 尝试SynchronizationContext 和 Post 方法,而不是老式的 Invoke。这可能会使 UI 调用更流畅。请参阅this answer 以了解其工作原理。

顺便说一句,如果您可以在第二个 Invoke 中移动以 .backcolor = Color.White 结尾的三行,则可以删除第一个 Invoke。

如果不是更好,那么尝试将构建项目和显示它们的过程分开:

  • 使用并行化填充要更新的项目列表(不是使用 ForEach,而是使用 .AsParallel() 针对 LINQ 查询)
  • 使用循环更新您的 ListView。

如果这不令人满意,您可能不得不在虚拟模式下使用 ListView 找到解决方法。 This answer 给出了一些以异步方式填充虚拟 ListView 的提示。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-27
    • 2014-12-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多