我正在编写一个程序,它从serialport接收数据并更新文本框,标签等UI元素。定时器用于经常向设备发送命令,响应设备返回显示的一些数据。一切都好。现在,如果我移动主窗体或最小化它,然后接收代码运行的线程退出(我认为)。移动主窗体是否可能导致线程退出?在我的程序中可能出现什么问题?为何数据接收停止?可行的解决方案?
答案 0 :(得分:1)
您正在更新resieving线程中的UI元素。
这意味着您必须在控件上调用UI更新调用,因为它们不是在另一个线程中创建的。
所以,我猜测接收者的工作方式如下:
while(!Application.IsApplicationExiting)
{
if (!serial.IsReady) break;
...
Data data = serial.GetData();
string text = GetBaudRate(data);
ui.Invoke((Label label) => label.Text = value, txtBauldRateLabel);
...
}
当时,你应该知道在这种情况下这个线程被阻塞,直到对UI线程的调用结束,但是当你试图调整大小或定位应用程序窗口时,相关部分是对UI线程的更新也会阻塞。
当您尝试在应用程序的tite栏上单击并按住鼠标右键时,UI线程会阻塞。
因此,您可以锁定接收器的主循环,在设备超时的情况下实际上不会以任何方式阻止它,这会导致接收器关闭(改变接收器的状态)连续到非准备好。)
我建议您使用对BeginInvoke()的解锁调用来重写您的应用程序:
while(!Application.IsApplicationExiting)
{
...
ui.BeginInvoke((Label label) => label.Text = value, txtBauldRateLabel);
...
}
答案 1 :(得分:0)
在大多数情况下,您的行为(移动/最小化表单)不会影响在代码中创建的线程。更新UI的数据线程(可能使用类似myControl.Invoke之类的东西)即使您正在移动表单或最小化表单也能正常工作(但如果关闭表单而没有更新线程中止,您将获得异常)。所以你不应该担心这一点。相反,我认为您需要检查移动事件背后的代码或最小化您的表单事件。提供一些代码有助于找出原因。
答案 2 :(得分:0)
有一个名为Threads的调试窗口。启动程序并在遇到断点时打开线程窗口。当您的线程收到数据时,应该至少列出两个线程ID,UI线程和线程获取和接收串行数据。您可能想要命名您的线程,以便在调试器中更容易看到,否则只会有线程ID。从那里,这应该提供一些线索,为什么或至少它何时退出。
在您的线程代码中放置断点以及要监控的UI代码。问题相当模糊,但是调试和观看线程会有所帮助。
答案 3 :(得分:0)
你正在崩溃计时器线程,因为你正在直接更新它的表单控件...你不能这样做!您必须调用Invoke(),以便您的代码从主UI线程执行。
我在你的另一个问题上发了一个答案......这里又是:
我的猜测是你正在崩溃你的计时器线程,因为你无法在没有问题的情况下从另一个线程更新Form控件......你必须创建一个在主UI线程下运行的委托。您可以通过测试Form.InvokeRequired
并调用Form.Invoke
来实现此目的。
发生了什么:您的计时器线程正在更新表单上的文本框或其他控件。您调整大小或最小化,并且这些表单控件的句柄无效...您不能再使用它们了。除了您的计时器线程仍在运行并尝试使用它们。崩溃!
多线程表单的一个很好的例子是here。重要的是:
delegate void SetBoolDelegate(bool parameter);
// This would be your timer tick event handler...
void SetInputEnabled(bool enabled) {
if(!InvokeRequired) {
button1.Enabled=enabled;
comboBoxDigits.Enabled=enabled;
numericUpDownDigits.Enabled=enabled;
}
else {
Invoke(new SetBoolDelegate(SetInputEnabled),new object[] {enabled});
}
}
在此示例中,您将测试InvokeRequired。如果它是假的,那么你在主UI线程上运行并可以直接设置控件属性。如果是,则调用Invoke(),传递将从主UI线程调用的函数。
在此示例中,您调用的函数/委托与您所在的函数相同,但并非必须如此。但是你可以传递计时器tick事件处理程序,让它在主线程上执行。