线程退出问题

时间:2011-01-14 06:36:13

标签: c#

我正在编写一个程序,它从serialport接收数据并更新文本框,标签等UI元素。定时器用于经常向设备发送命令,响应设备返回显示的一些数据。一切都好。现在,如果我移动主窗体或最小化它,然后接收代码运行的线程退出(我认为)。移动主窗体是否可能导致线程退出?在我的程序中可能出现什么问题?为何数据接收停止?可行的解决方案?

4 个答案:

答案 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事件处理程序,让它在主线程上执行。