除了一个问题,我手头的任务几乎完成了。我正在尝试通过 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);
}
}
最佳答案
这很复杂。您有 backgroundWorker1
,它似乎只是向 UI 线程发送一条消息(通过 Control.BeginInvoke
)指示它向 添加一个项目列表框
。同时,backgroundWorker4
只是向 UI 线程发送消息,指示它调用 BeginUpdate
和 EndUpdate
。
UI 更新仍在 UI 线程上进行。一方面这很好,因为您绝对不能从创建它的线程以外的线程访问 UI 元素。但是,由于您的工作线程只是向 UI 线程发送消息,所以它们几乎毫无意义。事实上,它实际上让事情变得更慢。
BeginUpdate
、EndUpdate
和Add
方法的顺序将完全随机。我打赌你不会得到你想要的行为。锁定(通过
Monitor.Enter
和Monitor.Exit
)也毫无意义。由于锁只在 UI 线程上获取,因此永远不会有任何争用。使用
Control.BeginInvoke
或Control.Invoke
来弥合 UI 和工作线程之间的差距被滥用了。个人认为这个话题是argumentum ad populum的牺牲品| .很多时候,最好让 UI 线程定期(通过System.Windows.Forms.Timer
)轮询工作线程正在更新的共享数据结构。
关于C# - 在多线程环境中使用 listbox.BeginUpdate/listbox.EndUpdate 不工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4467732/