在关闭主窗体之前关闭侧面线程

Hto*_*onS 4 c# user-interface multithreading visual-studio-2010

我正在使用Visual Studio .NET 2010中的Windows窗体创建GUI应用程序C#.我有多个线程,例如:

  1. 正在ping目标设备的线程,以确保连接不会丢失
  2. 从设备请求和接收测量结果的线程
  3. 一个不断用这些结果更新主窗体的线程.此线程使用调用主窗体控件中的更改Control.Invoke().我的目标:当用户在主窗体上按(x)时,我想确保正确关闭窗体.这意味着我希望我的连接关闭,所有侧线程终止等.我正在尝试使用FormClosing事件.在那里我将KillThreads标志设置为true并等待侧线程终止.每个都有一张支票,比如
if(KillThreads)
    return;
Run Code Online (Sandbox Code Playgroud)

但是有一个问题.如果我试图等待所有线程终止

for(;;)
if(ActiveSideThreads == 0)
    break;
closeConnection();
Run Code Online (Sandbox Code Playgroud)

线程N3冻结被阻塞Invoke(); 当然是,因为主gui线程处于无限循环中.所以表格结束会永远延迟.

如果我将on-close操作实现为线程过程4,并在FromClosing事件处理程序中创建一个新线程,则表单在线程4终止之前立即关闭,并Invoke()抛出错误-t.4无法及时关闭线程3.如果我Join(), Invoke()再次阻止添加呼叫,并且线程3永远不会终止,那么线程4将永远等待.

我该怎么办?有什么方法可以解决这个问题吗?

Bri*_*eon 6

首先决定你是否真的需要使用它Invoke.它现在已经有一段时间了,我认为这Invoke是在工作线程启动后在UI线程上发生操作的最过度使用的技术之一.另一种方法是在工作线程中创建某种类型的消息对象,描述需要在UI线程上执行的操作并将其放入共享队列.然后,UI线程将在某个时间间隔内轮询此队列System.Windows.Forms.Timer,并使每个操作发生.这有几个优点.

  • 它打破了Invoke强加的UI和工作线程之间的紧密耦合.
  • 它将更新UI线程的责任放在它应该属于的UI线程上.
  • UI线程可以决定更新的发生时间和频率.
  • UI消息泵没有溢出的风险,就像工作线程启动的编组技术一样.
  • 在继续执行下一步之前,工作线程不必等待确认已执行更新(即,您在UI和工作线程上获得更多吞吐量).
  • 这是一个很多好办了关机操作,因为你几乎可以消除所有的竞争条件,要求工作线程可以正常终止时,通常会出现.

当然,Invoke非常有用,并且有很多理由继续使用这种方法.如果您决定继续使用,Invoke请阅读更多内容.

一个想法是KillThreadsForm.Closing事件中设置标志,然后通过设置取消关闭表单FormClosingEventArgs.Cancel = true.您可能希望让用户知道已请求关闭并且正在进行中.您可能希望禁用表单上的某些控件,以便无法启动新操作.所以基本上我们发信号通知已经请求了关闭,但是推迟关闭表单直到工作线程先关闭.要做到这一点,你可能想要启动一个定时器,定期检查工作线程是否已经结束,如果有,你可以调用Form.Close.

public class YourForm : Form
{
  private Thread WorkerThread;
  private volatile bool KillThreads = false;

  private void YourForm_Closing(object sender, FormClosingEventArgs args)
  {
    // Do a fast check to see if the worker thread is still running.
    if (!WorkerThread.Join(0))
    {
      args.Cancel = true; // Cancel the shutdown of the form.
      KillThreads = true; // Signal worker thread that it should gracefully shutdown.
      var timer = new System.Timers.Timer();
      timer.AutoReset = false;
      timer.SynchronizingObject = this;
      timer.Interval = 1000;
      timer.Elapsed = 
        (sender, args) =>
        {
          // Do a fast check to see if the worker thread is still running.
          if (WorkerThread.Join(0)) 
          {
            // Reissue the form closing event.
            Close();
          }
          else
          {
            // Keep restarting the timer until the worker thread ends.
            timer.Start();
          }
        };
      timer.Start();
    }
  }    
}
Run Code Online (Sandbox Code Playgroud)

上面的代码调用Join,但它指定超时为0,这会导致Join调用立即返回.这应该保持UI抽取消息.