C# 当主窗体最小化或移动时,从serialport接收数据停止

C# 当主窗体最小化或移动时,从serialport接收数据停止,c#,C#,先生 我很抱歉,如果这已经包括在某个地方。我做了一次搜索,找到了一些我已经实现的东西 我正在开发一个应用程序,它从通过串口连接的设备接收数据。我正在使用SerialPort datareceived事件捕获数据。我将在主窗体的文本框中显示数据。经常收到数据。我用定时器向设备发送命令,响应设备发送一些数据。定时器间隔为100毫秒。在每100毫秒的间隔内,发送一些命令并相应地接收数据。我使用调用函数来更新GUI元素,如文本框、标签等。一切都很好。所有元素都在漂亮地更新。但是在接收数据的过程中,如果我

先生

我很抱歉,如果这已经包括在某个地方。我做了一次搜索,找到了一些我已经实现的东西

我正在开发一个应用程序,它从通过串口连接的设备接收数据。我正在使用SerialPort datareceived事件捕获数据。我将在主窗体的文本框中显示数据。经常收到数据。我用定时器向设备发送命令,响应设备发送一些数据。定时器间隔为100毫秒。在每100毫秒的间隔内,发送一些命令并相应地接收数据。我使用调用函数来更新GUI元素,如文本框、标签等。一切都很好。所有元素都在漂亮地更新。但是在接收数据的过程中,如果我在主窗体中做了任何更改,比如移动窗体、最小化、最大化或单击窗体中的某个位置,那么数据接收就会停止。我找不到它发生的原因?我还将计时器间隔更改为200300400500,但同样的问题也存在

请告诉我为什么会这样?和可能的解决方案


提前感谢……)

为什么不在另一个线程上执行此操作并查看。

为什么不在另一个线程上执行此操作并查看。

问题是最小化时计时器被禁用。您应该创建一个新的
线程
,并在其中使用
Thread.Sleep(100)使其睡眠。另外,在关闭时中止它。考虑一下:

Thread recieverThread = new Thread(delegate()
  {
    try
    {
      //try loading data
      Thread.Sleep(100);
    }
    catch (ThreadAbortException)
    {
      //close port or something
    }
  });

//on form.close or something like that
recieverThread.Abort();

这应该能奏效。另外,如果Receiver更新了接口,您必须使用
表单。调用(…)
来执行此操作,因为它在单独的线程上运行。

问题是最小化时计时器被禁用。您应该创建一个新的
线程
,并在其中使用
Thread.Sleep(100)使其睡眠。另外,在关闭时中止它。考虑一下:

Thread recieverThread = new Thread(delegate()
  {
    try
    {
      //try loading data
      Thread.Sleep(100);
    }
    catch (ThreadAbortException)
    {
      //close port or something
    }
  });

//on form.close or something like that
recieverThread.Abort();

这应该能奏效。另外,如果Receiver更新接口,您必须使用
表单。Invoke(…)
来执行此操作,因为它在单独的线程上运行。

我猜您正在破坏计时器线程,因为您无法从另一个线程更新表单控件而不会出现问题。。。您必须创建一个在主UI线程下运行的委托。您可以通过测试
Form.invokererequired
并调用
Form.Invoke
来实现这一点

发生的情况:计时器线程正在更新表单上的文本框或其他控件。调整大小或最小化,这些表单控件的句柄将无效。。。你不能再使用它们了。除非您的计时器线程仍在运行并尝试使用它们。撞车

关于多线程表单的一个很好的例子是。重要的部分是:

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。如果为false,那么您正在主UI线程上运行,可以直接设置控件属性。如果为true,则调用Invoke(),传递将从主UI线程调用的函数


在本例中,您调用的函数/委托与您所在的函数相同,但不一定非得如此。但是,您可以将timer tick事件处理程序传递给它,使其在主线程上执行。

我猜您正在破坏计时器线程,因为您无法从另一个线程更新表单控件而不会出现问题。。。您必须创建一个在主UI线程下运行的委托。您可以通过测试
Form.invokererequired
并调用
Form.Invoke
来实现这一点

发生的情况:计时器线程正在更新表单上的文本框或其他控件。调整大小或最小化,这些表单控件的句柄将无效。。。你不能再使用它们了。除非您的计时器线程仍在运行并尝试使用它们。撞车

关于多线程表单的一个很好的例子是。重要的部分是:

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。如果为false,那么您正在主UI线程上运行,可以直接设置控件属性。如果为true,则调用Invoke(),传递将从主UI线程调用的函数


在本例中,您调用的函数/委托与您所在的函数相同,但不一定非得如此。但是,您可以将timer tick事件处理程序传递给它,使其在主线程上执行。

您的UI线程在窗口移动操作期间阻塞在模式循环中。因此,任何
Form.Invoke()
call也会阻塞

最好的选择是在接收数据的后台线程之外使用队列。然后,UI线程只在计时器事件期间轮询数据

队列访问必须同步以避免竞争条件。当UI线程需要获取要应用的更新集时交换队列实例,因为UI可能需要执行多个耗时的操作

类似这样的队列管理器将起作用(两个线程本质上都拥有一个队列,并且管理器会切换它们):

专用队列数据队列;
私有对象syncLock=新对象();
私有易失性bool数据可用;//易失性,以避免读取时锁定。
//由后台线程调用以将事件对象(例如字符串)添加到队列。
无效队列输出(T数据)
{
锁(this.syncLock)
{
if(this.dataQueue==null)
{
this.dataQueue=新队列();
}
this.dataQueue.Add(数据);
this.dataAvailable=true;
}
}
//由UI线程调用以获取挂起的更新。
Queue QueueGet(Queue oldQueue)
{
if(oldQueue!=null)
{
oldQueue.Clear();
}
排队结果;
锁(this.syncLock)
{
结果=this.dataQueue;
this.dataQueue=oldQueue;
this.dataAvailable=false;
}
返回结果;
}
//由UI线程调用以避免检索空队列
//(以及随后的重新分配)。
公共bool IsDataAvailable()
{
得到
{
返回此.dataAvailable;
}
}