C#-在多线程环境中使用listbox.BeginUpdate/listbox.EndUpdate不起作用

C#-在多线程环境中使用listbox.BeginUpdate/listbox.EndUpdate不起作用,c#,winforms,multithreading,C#,Winforms,Multithreading,除了一个问题外,我手头的任务几乎完成了。我正试图通过beginupdate()和endupdate()控制listbox ui的更新,这两种方法都是通过backgroundWorker线程实现的,backgroundWorker线程也用于更新我的进度条。我想,在items列表上设置一个锁或监视器就足够了(如果在绘图时需要解析列表),但是没有用。有人有什么想法吗 这是相关的代码 编辑:显示通过另一个线程向列表中添加的项目 private void backgroundWorker4_DoWork(

除了一个问题外,我手头的任务几乎完成了。我正试图通过beginupdate()和endupdate()控制listbox ui的更新,这两种方法都是通过backgroundWorker线程实现的,backgroundWorker线程也用于更新我的进度条。我想,在items列表上设置一个锁或监视器就足够了(如果在绘图时需要解析列表),但是没有用。有人有什么想法吗

这是相关的代码

编辑:显示通过另一个线程向列表中添加的项目

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);
        }
    }
private void backgroundWorker4\u DoWork(对象发送方,DoWorkEventArgs e)
{
//获取引发此事件的BackgroundWorker。
BackgroundWorker worker=发件人作为BackgroundWorker;
//间隔数
int stop=60;
对于(int i=1;i listBoxBeginUpdate(varListBox));
}
其他的
{
//请求锁,并阻止,直到获得锁为止。
Monitor.Enter(varListBox);
尝试
{
//获得锁后,添加一个元素。
varListBox.BeginUpdate();
}
最后
{
//确保锁已松开。
Monitor.Exit(varListBox);
}
}
}
公共静态无效ListBox更新(System.Windows.Forms.ListBox varListBox)
{
if(varListBox.invokererequired)
{
BeginInvoke(新方法调用程序(()=>ListBoxenUpdate(varListBox));
}
其他的
{
//请求锁,并阻止,直到获得锁为止。
Monitor.Enter(varListBox);
尝试
{
//获得锁后,添加一个元素。
EndUpdate();
}
最后
{
//确保锁已松开。
Monitor.Exit(varListBox);
}
//锁(varListBox.Items)
//{
//Monitor.Enter(变量列表
//EndUpdate();
//}
}
}
//添加以显示将项目添加到列表中的线程
私有void backgroundWorker1\u DoWork(对象发送方,DoWorkEventArgs e)
{
//获取引发此事件的BackgroundWorker。
BackgroundWorker worker=发件人作为BackgroundWorker;
随机=新随机();
//秒表秒表=新秒表();
//秒表。开始();
当(_线程运行时)
{
if(工作人员取消挂起)
{
e、 取消=真;
返回;
}
系统线程线程睡眠(1000);
int numberOfItems=random.Next(5,10);
对于(int i=5;ilistBoxAddItem(varListBox,项));
}
其他的
{
varListBox.Items.Add(item);
}
}

这相当复杂。您有
backgroundWorker1
,它似乎只是在向UI线程发送消息(通过
控件.BeginInvoke
)同时,
backgroundWorker4
只是向UI线程发送消息,指示它调用
BeginUpdate
EndUpdate

  • UI线程上仍在进行UI更新。从一个方面来说,这很好,因为除了创建UI元素的线程之外,您绝对无法从其他线程访问UI元素。但是,由于工作线程只向UI线程发送消息,因此它们几乎完全没有意义。事实上,它实际上y使事情变得更慢

  • BeginUpdate
    EndUpdate
    Add
    方法的顺序将是完全随机的。我打赌你不会得到你想要的行为

  • 锁(通过
    Monitor.Enter
    Monitor.Exit
    )也是毫无意义的。由于锁只在UI线程上获得,因此从不存在任何争用

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


列表本身为空,但您可以看出,由于滚动条的增长,项目仍在添加到列表中(这是预期的)。循环完成后列表才会自动重新填充。虽然我刚刚用5秒计时器测试了循环,但它在4秒内重新填充。我是否缺少它?将项目添加到列表框的位置?我故意忽略了。项目是通过threadsafe manor ju中的另一个线程循环添加到列表框的我喜欢调用静态listBoxBeginUpdate方法。我可以肯定地补充一点,如果它有助于澄清问题的话。我不完全清楚您要实现什么,但这看起来是一种非常复杂的方式。您到底想做什么?手头的问题是复杂的,但这是一个真正的问题on.Edit:意外按下enter太快,所以我也认为轮询共享数据结构会更好