【问题标题】:C# - Using listbox.BeginUpdate / listbox.EndUpdate in a multithreaded environment not workingC# - 在多线程环境中使用 listbox.BeginUpdate / listbox.EndUpdate 不起作用
【发布时间】:2010-12-17 04:38:14
【问题描述】:

我手头的任务几乎完成了,除了一个问题。我正在尝试通过 beginupdate() 和 endupdate() 通过 backgroundWorker 线程控制列表框 ui 的更新,该线程也用于更新我的进度条。我在想项目列表上的锁或监视器就足够了(在绘制时需要解析列表的情况下),但无济于事。有人有什么想法吗?

这是相关代码...

编辑:显示通过另一个线程将项目添加到列表中。

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

【问题讨论】:

  • 列表本身是空白的,尽管您可以看出由于滚动条的增长,项目仍在添加到其中(这是预期的)。直到循环完成后,列表才会重新填充。虽然我只是用 5 秒计时器测试了循环,但它会在大约 4 秒后重新填充。
  • 我错过了吗?添加到列表框中的项目在哪里?
  • 我故意忽略了它。项目正在通过线程安全庄园中的另一个线程循环添加到列表框,就像调用静态 listBoxBeginUpdate 方法一样。我可以肯定地补充一点,如果它有助于澄清事情的话。
  • 我并不完全清楚您要完成什么,但这看起来是一种非常复杂的方法。你到底想做什么?
  • 手头的问题是一个复杂的问题,但它是有原因的。编辑:太早不小心按下了输入

标签: c# winforms multithreading


【解决方案1】:

这是相当复杂的。你有backgroundWorker1,它似乎只是向 UI 线程(通过Control.BeginInvoke)发送一条消息,指示它向ListBox 添加一个项目。同时,backgroundWorker4 只是向 UI 线程发送消息,指示它调用 BeginUpdateEndUpdate

  • UI 线程的更新仍在进行。一方面这很好,因为您绝对不能从创建它的线程以外的线程访问 UI 元素。但是,由于您的工作线程只是向 UI 线程发送消息,因此它们几乎完全没有意义。事实上,它实际上让事情变得更慢。

  • BeginUpdateEndUpdateAdd 方法的顺序将完全随机。我打赌你不会得到你所追求的行为。

  • 锁(通过Monitor.EnterMonitor.Exit)也毫无意义。由于锁只在 UI 线程上获取,因此永远不会发生任何争用。

  • 过度使用Control.BeginInvokeControl.Invoke 来弥合UI 和工作线程之间的差距。个人觉得这个话题是argumentum ad populum的受害者。很多时候,最好让 UI 线程定期轮询工作线程正在更新的共享数据结构(通过System.Windows.Forms.Timer)。

【讨论】:

  • 所以我也相信轮询共享数据结构会更好。不太复杂,更容易控制 imo 的信息来源。同样,如果您阅读了我对原始问题的最后评论,您会发现此代码是这样编写的,因为问题本身要求它。问题要求一个线程向 UI 线程发送消息,另一个线程更新进度条并每半秒更新一次列表内容。
猜你喜欢
  • 2010-12-06
  • 2010-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-29
相关资源
最近更新 更多