我想构建一个程序,它将在单独的线程(UI线程除外)中每100毫秒与一些外部设备通信。我需要的是一些持续汇集的机制。
这是我第一次遇到多线程,我不知道如何正确地做到这一点......
据我所知,我可以使用System.Threading.Timer
类来阻止用户GUI。
不幸的是,我的TimerCallback
方法一开始就遇到了问题。我正在尝试使用System.Threading.Timer
计时器。在调用TimerCallback
程序突然退出后不久......我不明白为什么。有人可以帮我解释一下这种情况吗?
我的代码:
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);
}
}
我不知道这种方法是否适合汇集外部设备。
也许有更好的方法来做到这一点。也许有人可以提示我一些解决方案?
无论如何 - 问题在于TimerCallback
方法。在调用DataGridView
控件后不久,整个程序退出。
我正在使用Visual Studio 2010,该项目是在Windows窗体中。
答案 0 :(得分:1)
while debugging program exits here ... Why
因为您试图从另一个不允许的线程访问图形控件,所以在尝试从另一个线程访问控件的任何属性或方法之前尝试使用Control.InvokeRequired
答案 1 :(得分:1)
程序退出是因为您尝试从不是其所有者的线程访问控件( DataGrid )(换句话说,您正在尝试非法的跨线程异常)。 / p>
如果要从其他线程更新某些UI控件的内容,则需要使用Invoke
(阻止调用)或BeginInvoke
(非阻塞调用)。
在SO(或在网络上)你应该找到大量的例子:如果你需要一些特定的信息,请再次写在这里。
答案 2 :(得分:1)
由于计时器线程中的UI更新是非法的,因此您应该更改更新操作。
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()); }));
}
答案 3 :(得分:1)
您可以使用Reactive Extensions RX-WinForms库。
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));
}
}