c# -线程中止和System.NullReferenceException

本文关键字:System NullReferenceException 线程 | 更新日期: 2023-09-27 18:05:57

我正在使用线程做一个练习GUI烤箱程序,我不确定我是否应该这样做,因为我想在加热过程正在进行时与GUI交互。当我试图通过点击btnStop_Click来中止线程时,它会抛出NullReference异常:

系统。

请建议我如何才能优雅地停止线程。谢谢。

代码:

 public partial class Form1 : Form
        {
            private Thread t;
            public Form1()
            {
                InitializeComponent();
            }
            // button to begin heating
            private void btnStart_Click(object sender, EventArgs e)
            {   
                if ((txtMin.Text) == "" || (txtSec.Text) == "")
                {
                    MessageBox.Show("Please enter duration of heating");
                }
                else
                {
                    t = new Thread(heatIt);
                    btnHeat.Enabled = false;
                    t.Start();
                }
            }
           //stop heating
            private void btnStop_Click(object sender, EventArgs e)
            {
                Heating heat = new Heating();
                Form1 l = new Form1();
                l.Subscribe(heat);
                heat.stopHeat();
                btnHeat.Enabled = true;
            }
            private void heatIt()
            {
            // heat food Implementation that calls the 'Heating' class
            }
           public void Subscribe(Heating m)
           {
            m.heatComplete += SignalHeatCompleted;
            m.heatStop += SignalStop;
           }
           private void SignalHeatCompleted(Heating m, EventArgs e)
           {
            MessageBox.Show( "Done, please enjoy your food");
            return;
           }
           private void SignalStop(Heating m, EventArgs e)
           {
           t.Abort();
            MessageBox.Show("Heating Terminated");
            return;            
           }
public class Heating
        {
            public event HeatingCompleted heatComplete; // Heating Completed Event
            public event HeatingStop heatStop;          // Heating Stop Event
            public EventArgs e = null;
            public delegate void HeatingCompleted(Heating h, EventArgs e);
            public delegate void HeatingStop(Heating s, EventArgs e);
            public void startHeat(int temp, int min, int sec)
            {
                int totalSec;
                totalSec = ((min*60) + sec) * 1000;
                Thread.Sleep(totalSec);
                if (heatComplete != null)
                {
                    heatComplete(this, e);
                }
                else
                {
                    //Use default signal if there's no subscription to this event
                    MessageBox.Show("*TING*");
                }
                return;    
            }
            public void stopHeat()
            {
                if (heatStop != null)
                {
                    heatStop(this, e);
                }
            }
        }
    }

c# -线程中止和System.NullReferenceException

你在停止点击事件中创建了一个新的Form1实例,所以你在与一个完全不同于开始点击事件的实例对话

你可能还想有一个单独的Heat实例,你在heatIt中分配,然后在你的停止点击中使用该引用。

对于后台处理,你可能想看看BackgroundWorker类来为你做繁重的工作。

几点说明:

  1. 你不应该使用Thread.Abort来停止后台任务。这是一个不好的做法,因为它强制终止后台线程,而不管它的状态如何。使用volatile bool标志,并检查(每隔一段时间)它的值是否发生了变化。

  2. 似乎你的表单代表了业务逻辑提取到一个单独的类(Heating)的UI。在这种情况下,每个表单只有一个单个实例并将其放在私有字段中可能是有意义的。现在你正在创建一个新的实例在你的Stop方法,这可能是错误的(因为我假设你已经在heatIt方法中使用它)。

  3. 对于每个Subscribe方法,尝试保持添加Unsubscribe方法的习惯,该方法在某些时候分离事件处理程序。这样,GC就可以在侦听器不再需要时收集它们,并且可以避免多次添加相同的事件处理程序。

我希望是这样的:

private Heating _heating;
private Thread _workerThread;
private volatile bool _stopRequest = false;
void Start_Btn_Pressed(object sender, EventArgs e)
{
    // create the private instance
    _heating = new Heating();
    Subscribe(_heating);
    // start the thread
    _stopRequest = false;
    _workerThread = new Thread(HeatIt);
    _workerThread.Start();
}
void Stop_Btn_Pressed(object sender, EventArgs e)
{
    // request stop
    _stopRequest = true;
    // wait until thread is finished
    _workerThread.Join();
    // unsubscribe
    // ** note that a better place for unsubscribing 
    //    might be at the end of the HeatIt method
    Unsubscribe(_heating);
}

并且,在您的后台工作方法中,您需要有一个循环来检查_stopRequest是否已设置:

void HeatIt()
{
    while (!_stopRequest && !finishedWork)
    {
        // do work
    }
}

注意必须在你的工作方法中有一个位置来检查_stopRequest标志。否则,停止它的唯一方法就是中止它(就像您所做的那样),这是不推荐的。

除此之外,一旦进程完成,您不需要停止线程(就像您在SignalStop方法中所做的那样)。当HeatIt方法返回(结束)时,线程也将结束,不需要这样做。