C# 一個簡單的秒錶引發的表單卡死問題

來源:互聯網
上載者:User

標籤:style   class   blog   code   http   tar   

一個秒錶程式也是我的一個心病,因為一直想寫這樣的一個東西,但是總往GUI那邊想,所以就比較怵,可能是上學的時候學MFC搞出的後遺症吧,不過當我今天想好用Win Form(話說還是第一次寫win form)寫這麼一個東西的時候,居然so easy。

所以說,做不了不可怕,怕的是你不去做,因為你不去做,你就永遠不知道你能不能做它。事實證明,大部分你猶豫能不能做的事情,實際上你都能搞定。

雖然成功實現了一個秒錶的簡易功能,即開始計時和停止。但是卻引發了一個關於win form和C#線程的問題。

下面一個一個來,先說一下秒錶的類實現

namespace Utils{    public class Time    {        private int _minute;        private int _second;        private bool _flag;//線程標識        private Thread _TimingThread = null;        public Time()        {            this._minute = 0;            this._second = 0;            this._flag = true;        }        /// <summary>        /// 開始計時        /// </summary>        public void Start()        {            if (_TimingThread == null)            {                _TimingThread = new Thread(new ThreadStart(AddSecond));                _TimingThread.Start();            }        }        /// <summary>        /// 線程執行方法        /// </summary>        private void AddSecond()        {            while(_flag)            {                Thread.Sleep(1000);                if (this._second == 59)                {                    this._minute++;                    this._second = 0;                }                else                {                    this._second++;                }            }        }        /// <summary>        /// 格式化顯示計時結果        /// </summary>        /// <returns></returns>        public string FormatTimeResult()        {            string minute = string.Empty;            string second = string.Empty;            if (this._minute < 10)            {                minute = "0" + this._minute.ToString();            }            else             {                minute = this._minute.ToString();            }            if (this._second < 10)            {                second = "0" + this._second.ToString();            }            else            {                second = this._second.ToString();            }            return minute + ":" + second;        }        /// <summary>        /// 停止        /// </summary>        public void Stop()        {            this._flag = false;        }        /// <summary>        /// 歸0操作        /// </summary>        public void Zero()        {            this._minute = 0;            this._second = 0;        }    }}

秒錶的實現還是比較簡單的,感覺這樣寫,也方便以後做擴充。

下面說說win form方面

表單就是這樣,一個label,兩個button

最開始,我寫了這樣一段代碼

    public partial class Form1 : Form    {        private Time mTime = null;        private Thread mDisplayThread = null;        public Form1()        {            InitializeComponent();            mTime = new Time();//執行個體化秒錶類        }        private void button_start_Click(object sender, EventArgs e)        {            mTime.Start();            mDisplayThread = new Thread(new ThreadStart(DisplayCurrentTime));            mDisplayThread.Start();            button_start.Enabled = false;        }        public void DisplayCurrentTime()        {            while (true)            {                Thread.Sleep(1000);                label_Time.Text = mTime.FormatTimeResult();//對Label標籤進行即時更新                Console.WriteLine("{0}", mTime.FormatTimeResult());            }        }        private void button_stop_Click(object sender, EventArgs e)        {            mTime.Stop();            button_start.Enabled = true;        }}

這樣寫感覺思路上沒什麼問題,當點擊【開始計時】按鈕的同時建立一個線程,而這個線程是用來每隔一秒去更新一下label上的顯示計時時間。

然而,之後卻報一個這樣的錯誤:Cross-thread operation not valid: Control ‘label_Time‘ accessed from a thread other than the thread it was created on.

網上查了一下,這個錯誤貌似很常見,MSDN上也給了一個出現此錯誤的原因,是這樣說的,當您試圖從單獨的線程更新一個win form時,會出現這個錯誤。

查了一下,就是說win form上的控制項屬性想要進行修改的時候,只能在建立Control的線程裡調用,不能在以外的線程被調用。而上面的

label_Time.Text = mTime.FormatTimeResult();

這段代碼呢恰恰是發生在新建立的線程之中,所以就會報錯了。

解決辦法是用delegate(委託)加上control.Invoke去聯合實現。下面看看實現部分

    public partial class Form1 : Form    {        private Time mTime = null;        private Thread mDisplayThread = null;        public delegate void UpdateLabel();//聲明一個委託        public UpdateLabel updateLabel;//定義一個委託        public Form1()        {            InitializeComponent();            mTime = new Time();            updateLabel = new UpdateLabel(UpdateTime);//執行個體化一個委派物件        }        private void button_start_Click(object sender, EventArgs e)        {            mTime.Start();            mDisplayThread = new Thread(new ThreadStart(DisplayTimeFunc));            mDisplayThread.Start();            button_start.Enabled = false;        }        /// <summary>        /// 線程執行方法        /// </summary>        public void DisplayTimeFunc()        {            while (true)            {                Thread.Sleep(1000);                this.Invoke(this.updateLabel);            }        }        /// <summary>        /// 單獨對Label進行重新整理        /// </summary>        public void UpdateTime()        {            label_Time.Text = mTime.FormatTimeResult();        }        private void button_stop_Click(object sender, EventArgs e)        {            mTime.Stop();            button_start.Enabled = true;        }    }

這段代碼裡mDisplayThread線程執行了DisplayTimeFunc方法,而DisplayTimeFunc方法裡實際就是在更新label,不同的是使用了Control.Invoke方法,上面不是說對控制項屬性的更改要在建立控制項的線程裡才執行嗎?現在看起來好像還是老樣子。那是因為我們不瞭解Control.Invoke是什麼東東。MSDN上的解釋是:在擁有此控制項的基礎視窗控制代碼的線程上執行指定的委託。OK,明白了,this.updateLabel這個委託最後還是在視窗建立的線程中執行的。

回頭想想,其實思路也比較簡單,就是先將更改控制項屬性的操作放在一個方法裡,然後寫個委託,再寫個線程,線上程的執行方法中調用這個委託就OK啦。

不過到這還不算全完,還有一個小問題,就是當我計時之後,想要關閉這個表單的時候,發現又開始報錯了:

Invoke or BeginInvoke cannot be called on a control until the window handle has been created.

研究了一下發現了出現此問題的原因,就是我們“上完廁所沒有擦PP”,上面的代碼中沒有一個操作是對 mDisplayThread 這個線程做了終止的動作。

所以我們還需要添加以下動作

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)        {            mDisplayThread.Abort();        }

這樣就完整了,在關閉Form1表單之前,先把線程終止。

做這個小東西的時候居然連帶著讓我瞭解了一些委託和Control.Invoke以及線程的知識點。我會找個時間好好把這部分看看的,爭取能總結點什麼出來。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.