C# - Using listbox.BeginUpdate / listbox.EndUpdate in a multithreaded environment not working
My task at hand is all but completed except for one issue. I'm trying to control the update of a listbox ui via beginupdate() and endupdate() via a backgroundWorker thread that is also being used to update my progress bar. I was thinking that a lock or monitor on the items list would suffice (in the case that the list needs to be parsed when drawing), but to no avail. Anybody have any ideas?
Here's the relevant code...
EDIT : To show adding of items to the list via another thread.
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);
开发者_如何学Python 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);
}
}
This is rather convoluted. You have backgroundWorker1
which is appears to be doing little more than sending a message to the UI thread (via Control.BeginInvoke
) instructing it to add an item to a ListBox
. Simultaneously, backgroundWorker4
is doing little more than sending messages to the UI thread instructing it to call BeginUpdate
and EndUpdate
.
The updates to the UI are still happening on the UI thread. In one respect that is good because you absolutely cannot access a UI element from a thread other than the one that created it. However, since your worker threads are doing nothing more than sending messages to the UI thread they are almost completely pointless. In fact, it actually makes things slower.
The sequencing of the
BeginUpdate
,EndUpdate
andAdd
methods are going to be completely random. I am betting you are not going to get the behavior you were after.The locks (via
Monitor.Enter
andMonitor.Exit
) are pointless as well. Since the lock is only ever acquired on the UI thread there is never any contention.The use of
Control.BeginInvoke
orControl.Invoke
to bridge the gap between the UI and worker threads is overused. Personally, I think this topic is the victim of argumentum ad populum. Many times it is better to have the UI thread poll a shared data structure periodically (viaSystem.Windows.Forms.Timer
) that the worker thread is updating.
精彩评论