C # - Использование listbox.BeginUpdate / listbox.EndUpdate в многопоточной среде не работает - PullRequest
3 голосов
/ 17 декабря 2010

Моя задача почти завершена, за исключением одного вопроса.Я пытаюсь контролировать обновление интерфейса списка через beginupdate () и endupdate () через поток backgroundWorker, который также используется для обновления моего индикатора выполнения.Я думал, что блокировки или монитора в списке элементов будет достаточно (в случае, если список должен быть проанализирован при рисовании), но безрезультатно.У кого-нибудь есть идеи?

Вот соответствующий код ...

РЕДАКТИРОВАТЬ: Чтобы показать добавление элементов в список через другой поток.

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

1 Ответ

2 голосов
/ 17 декабря 2010

Это довольно запутанно. У вас есть backgroundWorker1, что, кажется, делает чуть больше, чем отправка сообщения в поток пользовательского интерфейса (через Control.BeginInvoke) с указанием добавить элемент в ListBox. Одновременно backgroundWorker4 делает немного больше, чем просто отправляет сообщения в поток пользовательского интерфейса, инструктируя его вызывать BeginUpdate и EndUpdate.

  • Обновления пользовательского интерфейса все еще происходят в потоке пользовательского интерфейса. С одной стороны, это хорошо, потому что вы абсолютно не можете получить доступ к элементу пользовательского интерфейса из потока, отличного от того, который его создал. Однако, поскольку ваши рабочие потоки не делают ничего, кроме отправки сообщений в поток пользовательского интерфейса, они почти полностью бессмысленны. На самом деле, это на самом деле делает вещи медленнее.

  • Последовательность методов BeginUpdate, EndUpdate и Add будет полностью случайной. Могу поспорить, что вы не получите то поведение, за которым следовали.

  • Замки (через Monitor.Enter и Monitor.Exit) также бессмысленны. Так как блокировка только когда-либо получена в потоке пользовательского интерфейса, никогда не будет никакого конфликта.

  • Использование Control.BeginInvoke или Control.Invoke для преодоления разрыва между пользовательским интерфейсом и рабочими потоками чрезмерно используется. Лично я считаю, что эта тема является жертвой argumentsum ad populum . Во многих случаях лучше, чтобы поток пользовательского интерфейса периодически опрашивал общую структуру данных (через System.Windows.Forms.Timer), которую обновляет рабочий поток.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...