You can use the marshaling techniques like Control.Invoke
to execute a delegate on the UI thread where UI elements can be safely manipulated, but that approach is not very good. Actually, it is a terrible approach if all you want to do is update simple progress information.
By far the best method for doing this is:
- Have your worker thread publish progress information to a shared variable.
- Have your UI thread poll for it via a
System.Windows.Forms.Timers
on an interval that works well for you.
Here is what it might look like.
public class Example : Form
{
private volatile int percentComplete = 0;
private void StartThreadButton_Click(object sender, EventArgs args)
{
StatusBarUpdateTimer.Enabled = true;
new Thread(
() =>
{
for (int i = 1; i <= 100; i++)
{
DoSomeWork();
percentComplete = i;
}
}).Start();
}
private void StatusBarUpdateTimer_Tick(object sender, EventArgs args)
{
yourStatusBarPanel.Text = percentComplete.ToString() + "%";
StatusBarUpdateTimer.Enabled = percentComplete < 100;
}
}
This works well because:
- The percentComplete field is declared 'volatile' ensuring its value can be reliably read from multiple threads.
- The UI thread gets to dictate when and how often the UI gets updated...the way it should be!
- The worker thread does not have to wait for a response from the UI thread before it can proceed as would be the case with
Invoke
.
- It breaks the tight coupling between the UI and worker threads that
Invoke
would impose.
- It is more efficient...considerably.
- You get more throughput on both the UI and worker threads.
- There is no chance of saturating the UI message queue as could be the case with
BeginInvoke
.
- You do not have to litter you code with
Invoke
calls everytime you need to update the UI from the worker thread.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…