0

I have a function which modifies my listview "inputList",the listview has 5 columns. The function is supposed to check the cells in the listview,the cells in the last column will have "NORMAL" with green background if there is nothing wrong and if there is something wrong, it will have "ERROR" with red background in the cell in the last column and also change the color of the cells that are wrong in the same line from the other columns.

There are 4k items, I know that it's pointless to have so many item since the user is not going to read them but I was asked to this way.

I'm using a function with backgroundworker but its not fast enough and thought parallelFor would be faster. But when I try the parallelFor, its freezing the program.

This is the function with backgroundworker which is working but its too slow:

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); }
            }));
        }
    }

And this is the function with ParallelFor, the program stops working even when I try a very small example with only items.

 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); }
            }));
        });
    }

How can I copy all items of the listview to a List without reference? I think adding/modify directly into the listview and the invokes are slowing the function so creating: List<ListViewItem> tList

I would modify everything in the tList and then I would use inputList.Items.AddRange(tList.ToArray()); This would remove all the invokes inside the loop.

    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());
    }));

But tItemsTemp,tItems,tList were all references... How can I copy with creating a reference?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Carlos Siestrup
  • 1,031
  • 2
  • 13
  • 33
  • 1
    "is not fast enough" "freezing the program" "is working"; these are all very vague terms, and you didn't ask a question. What's the problem? What are you getting? What are you expecting? What have you tried? Have you searched for anything similar and tried suggestions from those posts? – sab669 Sep 23 '15 at 17:23
  • This is related to your previous question. It still looks an awful amount of GUI work going on. Why not try this: stop using the BackgroundWorker and profile your code to see *which part* is the time consuming part. Right now, you are just throwing darts in a background thread hoping it speeds things up. That rarely works. I'm guessing you would be better off just turning off the drawing of the control until after you updated everything. – LarsTech Sep 23 '15 at 17:28
  • Without [a good, _minimal_, _complete_ code example](https://stackoverflow.com/help/mcve) that reliably reproduces the problem, it's impossible to know for sure what the problem is. However, based on the small amount of code shown here, it seems likely that you've either deadlocked the GUI thread (i.e. by calling `Parallel.For()` from there), or you are overloading it (i.e. by having an extremely high number of calls to `Invoke()` in a short period of time). Either way, please improve the question so that it is answerable. – Peter Duniho Sep 23 '15 at 17:35
  • @sab669 The program stops working without showing eny erro/exception. Is not fast enough is because its taking too long. – Carlos Siestrup Sep 23 '15 at 17:37
  • Edited the question with more details. – Carlos Siestrup Sep 23 '15 at 17:47

1 Answers1

0

Mixing UI and parallelization is not guaranteed to provide good results. The UI is a bottleneck by itself because it runs on a unique thread. Worse, yours includes some disk IO operations (File.Exists).

There are some things you may want to try anyway:

  • Enclose the whole loop with an inputList.BeginUpdate and an inputList.EndUpdate. This prevents the list to refresh each time you add an item.

  • Try SynchronizationContext and the Post method instead of the old fashioned Invoke. This might make the UI invocation smoother. See this answer to check how it works.

By the way, you can remove the first Invoke, if it is possible for you to move the three lines that ends with .backcolor = Color.White in the second Invoke.

If it is not better, then try to separate the process of building the items and displaying them :

  • Use parallelization fill a List of items to update (not with a ForEach, but with .AsParallel() against a LINQ query)
  • Use a loop to update your ListView.

If this is not satisfying, you will probably have to find a workaround using a ListView in virtual mode. This answer gives some hints to fill a virtual ListView an asynchronous way.

Community
  • 1
  • 1
Larry
  • 17,605
  • 9
  • 77
  • 106