簡體   English   中英

C# - 線程中止和 System.NullReferenceException

[英]C# - Thread Abort and System.NullReferenceException

我正在使用線程做一個練習 GUI Oven 程序,我不確定我是否應該這樣做,因為我想在加熱過程正在進行時與 GUI 交互。 當我嘗試通過單擊 btnStop_Click 中止線程時,它會引發 NullReference 異常:

System.NullReferenceException:Object 引用未設置為 object 的實例。

請就如何優雅地停止線程提出建議。 謝謝。

代碼:

 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);
                }
            }
        }
    }

您正在停止單擊事件中創建 Form1 的新實例,因此您正在與與開始單擊中的 t 完全不同的 t 交談。

您可能還希望擁有一個在 heatIt 中分配的 Heat 實例,然后在停止單擊中使用該引用。

同樣對於后台處理,您可能希望查看BackgroundWorker class來為您完成繁重的工作。

幾點說明:

  1. 您永遠不應該使用Thread.Abort來停止后台任務。 這是一個不好的做法,因為它會強制中止后台線程,而不管其 state。 改為使用volatile bool標志,並檢查(每隔一段時間)其值是否已更改。

  2. 您的表單似乎代表了提取到單獨的 class ( 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方法返回(結束)時,線程也將結束,無需這樣做。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM