我的任务除了一个问题外几乎完成。我正在尝试通过beginupdate()和endupdate()通过后台工作线程来控制列表框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);
}
}
答案 0 :(得分:2)
这相当令人费解。您有backgroundWorker1
似乎只是向UI线程发送消息(通过Control.BeginInvoke
)指示它将项目添加到ListBox
。同时,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
)工作线程正在更新的共享数据结构会更好。