Reputation: 99
I would like to build a program, which will communicate with some external device every 100 milliseconds in separate thread (other than UI thread). What I need is some mechanism of continual pooling.
This is my first encounter with multithreading and I don't know how to do it properly...
As far as I read I could use System.Threading.Timer
class to not block user GUI.
Unfortunately I have problems at the beginning with my TimerCallback
method. I'm trying to use System.Threading.Timer
timer. Shortly after invoking TimerCallback
program suddenly exits ... I don't understand why. Could someone help me explain this situation?
My code:
public partial class Form1 : Form
{
System.Threading.Timer timer;
TimerCallback tcb;
private void btnButton_Click(object sender, EventArgs e)
{
tcb = new TimerCallback(PoolingStart);
timer = new System.Threading.Timer(tcb, null, 0, Timeout.Infinite);
}
public void PoolingStart(object state)
{
dgvGrid.Rows.Add(); //while debugging program exits here ... Why ?
//some API for external device which retrieve data from it
timer.Change(0, 100);
}
}
I don't know if this approach is proper regarding pooling external devices though.
Maybe there is a much better way to do this. maybe someone could prompt me some solution?
Anyway - here the problem is with TimerCallback
method. The whole program exits shortly after calling DataGridView
control.
I'm using Visual Studio 2010 and the project is in Windows Forms.
Upvotes: 0
Views: 554
Reputation: 21255
You can use Reactive Extensions RX-WinForms library.
public partial class Form1 : Form
{
private const int PollIntervalMilliseconds = 100;
private readonly Task _backgroundPoll;
public Form1()
{
InititalizeComponents();
_backgroundPoll = StartBackgroundPoll();
}
private Task StartBackgroundPoll()
{
return Observable
.Interval(TimeSpan.FromMilliseconds(PollIntervalMilliseconds))
.Select(_ => GetData())
.ObserveOn(gdvGrid)
.ForEachAsync(data => gdvGrid.Rows.Add(data));
}
}
Upvotes: 1
Reputation: 249
Because the UI update in timer thread is illegal, you should change your update operation.
private void btnButton_Click(object sender, EventArgs e)
{
timer = new System.Threading.Timer(new TimerCallback(PoolingStart));
timer.Change(0, 100);
}
public void PoolingStart(object state)
{
this.dgvGrid.Invoke(new MethodInvoker(() => { this.dgvGrid.Rows.Add(new DataGridViewRow()); }));
}
Upvotes: 1
Reputation: 2734
The program exits because you are trying to access a control (the DataGrid) from a thread which is not the its owner (in other words you are experimenting an illegal cross-thread exception).
If you want to update the content of some UI controls from a different thread then you need to use Invoke
(blocking call) or BeginInvoke
(non-blocking call).
Here on SO (or on the web in general) you should find tons of examples about that: if you need some specific info write here again.
Upvotes: 1
Reputation: 13764
while debugging program exits here ... Why
Because you are trying to access a graphic control from another thread which is not allowed try to use Control.InvokeRequired
before you try access to any property or method of a control from another thread
Upvotes: 1