Reputation: 6498
I have heard that thread cannot access controls of other threads directly.
So our professor gave us a snippet
private void UpdateUI()
{
if(this.InvokeRequired)
this.Invoke(new MethodInvoker(UpdateUI));
else
this.Refresh();
}
and said that InvokeRequired
property returns false it the thread is not the owner of the control and then we should call Invoke()
method to tell the owner thread to execute UpdateUI()
method. Which then updates the UI.
But recently, just out of curiosity I commented InvokeRequired
and Invoke()
private void UpdateUI()
{
//if(this.InvokeRequired)
//this.Invoke(new MethodInvoker(UpdateUI));
//else
this.Refresh();
}
and was surprised to see that ThreadPool could access controls of another thread and now I feel that I haven't completely understood the concept of ThreadPool.
Here's is the complete code.
using System;
using System.Threading;
using System.Drawing;
using System.Windows.Forms;
class MainForm : Form
{
public MainForm()
{
this.Text = "Hello WinForms";
ThreadPool.QueueUserWorkItem(Clock);
}
private void Clock(object state)
{
for(;;)
{
Thread.Sleep(1000);
UpdateUI();
}
}
private void UpdateUI()
{
//if(this.InvokeRequired)
// this.Invoke(new MethodInvoker(UpdateUI));
//else
this.Refresh();
}
protected override void OnPaint(PaintEventArgs pe)
{
using(Pen pen = new Pen(Color.Red, 2))
pe.Graphics.DrawRectangle(pen, 20, 20, 125, 30);
pe.Graphics.DrawString(DateTime.Now.ToString(), this.Font, Brushes.Blue, 25, 30);
}
[STAThread]
public static void Main()
{
Application.Run(new MainForm());
}
}
Could someone please explain me how does this happen?
Thanks.
Upvotes: 3
Views: 2206
Reputation: 1936
You can access from other thread to elements, but it's not safe.
If several thread try to access you control concurrently it throws an exception. Because WF controls not thread safe.
Upvotes: 1
Reputation: 93464
In the application you posted, you are not accessing any controls from the ThreadPool, you are only calling Refresh on the form. This actually sends a message to the form to tell itself to redraw itself, but that message is received on the main GUI thread rather than from the ThreadPool thread.
Therefore, you need not Invoke in your case because you do not do any cross thread activity. The OnPaint method gets called indirectly via the windows message pump rather than directly from the Refresh method.
If you were to try and, for example, set the text of a textbox from a background thread... it would throw an exception, and you would be required to use the Invoke pattern to make it work.
Upvotes: 3
Reputation: 7672
The ThreadPool
is a pool of reusable threads that you can use to execute code. The ThreadPool threads run on a separate thread to the UI thread.
You're concerned with the UI thread. Any thread can access the UI thread, however, it is not thread safe. That's the key word here. It might work, it might not. If it does, well, you had good fortune.
Not thread safe simply means that you are not guaranteed a consistent and expected behaviour. Setting something now might work, but a millisecond later, it might fail randomly without reason.
With WinForms, .NET by default checks for cross-thread calls, but we can disable this and access the UI in a non-thread safe context by setting System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls
to false
. However, disabling this means that we need to expect and cater for random behaviour.
Upvotes: 3