开发者

C# - Using listbox.BeginUpdate / listbox.EndUpdate in a multithreaded environment not working

My task at hand is all but completed except for one issue. I'm trying to control the update of a listbox ui via beginupdate() and endupdate() via a backgroundWorker thread that is also being used to update my progress bar. I was thinking that a lock or monitor on the items list would suffice (in the case that the list needs to be parsed when drawing), but to no avail. Anybody have any ideas?

Here's the relevant code...

EDIT : To show adding of items to the list via another thread.

private void backgroundWorker4_DoWork(object sender, DoWorkEventArgs e)
    {
        // Get the BackgroundWorker that raised this event.
        BackgroundWorker worker = sender as BackgroundWorker;

        // Number of intervals
        int stop = 60;

        for (int i = 1; i <= stop; i++)
        {
            if (worker.CancellationPending)
            {
                e.Cancel = true;
                backgroundWorker4.ReportProgress(0);
                return;
            }

            //listBoxBeginUpdate(listBox1);

            // Half second intervals
            //listBox1.BeginUpdate();
            //listBox1.EndUpdate();
            //ListBox.listBoxBeginUpdate(listBox1); 
            listBoxBeginUpdate(listBox1);
            Thread.Sleep(500);
            listBoxEndUpdate(listBox1);

            listBoxBeginUpdate(listBox1);
            Thread.Sleep(500);
            listBoxEndUpdate(listBox1);

            // Update every second
            //listBoxEndUpdate(listBox1);

            int progress = i * 100 / stop;
            backgroundWorker4.ReportProgress(progress);

            //updateProgressBar = !updateProgressBar;
        }
    }
public static void listBoxBeginUpdate(System.Windows.Forms.ListBox varListBox)
    {
        if (varListBox.InvokeRequired)
        {
            varListBox.BeginInvoke(new MethodInvoker(() => listBoxBeginUpdate(varListBox)));
        }
        else
        {
            // Request the lock, and block until it is obtained.
            Monitor.Enter(varListBox);
            try
            {
                // When the lock is obtained, add an element.
                varListBox.BeginUpdate();
            }
            finally
            {
                // Ensure that the lock is released.
                Monitor.Exit(varListBox);
            }
        }
    }

    public static void listBoxEndUpdate(System.Windows.Forms.ListBox varListBox)
    {
        if (varListBox.InvokeRequired)
        {
            varListBox.BeginInvoke(new MethodInvoker(() => listBoxEndUpdate(varListBox)));
        }
        else
        {
              // Request the lock, and block until it is obtained.
              Monitor.Enter(varListBox);
            开发者_如何学Python  try
              {
                 // When the lock is obtained, add an element.
                  varListBox.EndUpdate();
              }
              finally
              {
                 // Ensure that the lock is released.
                  Monitor.Exit(varListBox);
              }

            //lock (varListBox.Items)
            //{
            //    Monitor.Enter(varList
            //    varListBox.EndUpdate();
            //}
        }
    }

// Added to show the thread adding items into the list
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        // Get the BackgroundWorker that raised this event.
        BackgroundWorker worker = sender as BackgroundWorker;
        Random random = new Random();

        //Stopwatch stopwatch = new Stopwatch();
        //stopwatch.Start();

        while(_threadsRunning)
        {
            if (worker.CancellationPending)
            {
                e.Cancel = true;
                return;
            }

            System.Threading.Thread.Sleep(1000);

            int numberOfItems = random.Next(5, 10);
            for (int i = 5; i < numberOfItems; i++)
            {
                int number = random.Next(1, 10000);
                listBoxAddItem(listBox1, number);
            }

            backgroundWorker1.ReportProgress(numberOfItems);
        }
    }

public static void listBoxAddItem(System.Windows.Forms.ListBox varListBox, int item)
    {
        if (varListBox.InvokeRequired)
        {
            varListBox.BeginInvoke(new MethodInvoker(() => listBoxAddItem(varListBox, item)));
        }
        else
        {
            varListBox.Items.Add(item);
        }
    }


This is rather convoluted. You have backgroundWorker1 which is appears to be doing little more than sending a message to the UI thread (via Control.BeginInvoke) instructing it to add an item to a ListBox. Simultaneously, backgroundWorker4 is doing little more than sending messages to the UI thread instructing it to call BeginUpdate and EndUpdate.

  • The updates to the UI are still happening on the UI thread. In one respect that is good because you absolutely cannot access a UI element from a thread other than the one that created it. However, since your worker threads are doing nothing more than sending messages to the UI thread they are almost completely pointless. In fact, it actually makes things slower.

  • The sequencing of the BeginUpdate, EndUpdate and Add methods are going to be completely random. I am betting you are not going to get the behavior you were after.

  • The locks (via Monitor.Enter and Monitor.Exit) are pointless as well. Since the lock is only ever acquired on the UI thread there is never any contention.

  • The use of Control.BeginInvoke or Control.Invoke to bridge the gap between the UI and worker threads is overused. Personally, I think this topic is the victim of argumentum ad populum. Many times it is better to have the UI thread poll a shared data structure periodically (via System.Windows.Forms.Timer) that the worker thread is updating.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜