天天看點

多線程中容易被忽略的界面假死的問題

界面假死的問題這兩天一直困擾着我,對着自己的代碼一遍一遍 的在尋找,但是始終不得其原因,後來再深入的到每個調用方法的時候,終于發現了一個細節沒有關注上,這個就是導緻我的界面卡死的問題的關鍵。

如下做個這個問題真相的還原!!

1、

  //輸出日志通知類資訊
        public  void WriteLogInfo(string info)
        {
            Thread.Sleep(50);
            ThreadMethodTxt(info);

            richTextBox1.Focus();
            richTextBox1.Select(richTextBox1.Text.Length, 0);
            richTextBox1.ScrollToCaret();
            richTextBox1.HideSelection = false;
            writetofile(info);
        }


       //建立一個委托。
        private delegate void UpdateTxt(string msg);
        //定義一個委托變量
        private UpdateTxt updateTxt;

        //修改richTextBox1值的方法。
        private void UpdateTxtMethod(string strInfo)
        {
            int n = this.richTextBox1.MaxLength;
            if (this.richTextBox1.TextLength < n)
            {
                RichTextBoxExtension.AutoSignColorAppendText(richTextBox1, Color.Red,strInfo);
            }
            else
                this.richTextBox1.Clear();

        }

        //此為在非建立線程中的調用方法。
        private void ThreadMethodTxt(string strInfo)
        {
            if (this.InvokeRequired)
                this.Invoke(updateTxt, new object[] { strInfo });
            else
                UpdateTxtMethod(strInfo);
        }

        private void HelpLog_Load(object sender, EventArgs e)
        {
            //執行個體化委托  richTextBox1控件
            updateTxt = new UpdateTxt(UpdateTxtMethod);
        }
           

 2、

public static class RichTextBoxExtension
    {
        public static void AutoSignColorAppendText(this RichTextBox rtbox, Color color,string info)
        {
            Font fn = new Font("Verdana", 13F, FontStyle.Bold, GraphicsUnit.Point);
            rtbox.Focus();
            rtbox.SelectionColor = color;
            rtbox.SelectionFont = fn;
            rtbox.SelectionColor = color;
            rtbox.SelectionBackColor = Color.Yellow;
            rtbox.AppendText(info + "\n");
            rtbox.Select(0, 0);
        }
    }

注意到 RichTextBoxExtension.AutoSignColorAppendText(richTextBox1, Color.Red,strInfo);
其中 RichTextBoxExtension 為靜态類,AutoSignColorAppendText為靜态方法。
           

3、标紅色的區域就是關鍵的地方。我這裡是用了靜态類中的方法的 richTextBox 控件的擴充使用。

4、我們在知道, 靜态字段(static field)和靜态方法(static method)的調用是通過類來調用,靜态方法不對特定的執行個體操作,隻能通路靜态成員。執行個體方法可對特定的執行個體操作,既能通路靜态成員,也能通路執行個體成員。

那麼,在多線程中使用靜态方法是否有線程安全問題?這要看靜态方法是是引起線程安全問題要看在靜态方法中是否使用了靜态成員。在多線程中使用同一個靜态方法時,每個線程使用各自的執行個體字段(instance field)的副本,而共享一個靜态字段(static field)。

是以說,如果該靜态方法不去操作一個靜态成員,隻在方法内部使用執行個體字段(instance field),不會引起安全性問題。但是,如果該靜态方法操作了一個靜态字段,則需要靜态方法中采用互斥通路的方式進行安全處理。

5、在回到我的代碼中,在非建立線程中要更新UI界面,就要使用 Invoke或者 BeginInvoke,用委托的方式去更新UI界面。

上面的代碼看起來好像沒有問題,運作起來短時間也不會出現問題,在運作長點時間後,問題就馬上出來了,要麼提示說“嘗試讀取或者寫入受保護的記憶體,這提示表明相關的記憶體已經損壞!”要麼界面就是卡死狀态。界面卡死就是多線程中同時調用了靜态方法。

解決方法:

public void AutoSignColorAppendText(this RichTextBox rtbox, Color color,string info)
        {
            Font fn = new Font("Verdana", 13F, FontStyle.Bold, GraphicsUnit.Point);
            rtbox.Focus();
            rtbox.SelectionColor = color;
            rtbox.SelectionFont = fn;
            rtbox.SelectionColor = color;
            rtbox.SelectionBackColor = Color.Yellow;
            rtbox.AppendText(info + "\n");
            rtbox.Select(0, 0);
        }
           

去掉static 字段,不在擴充靜态類中調用。

6、注意:靜态變量,由于是在類加載時占用一個存儲區,每個線程都是共用這個存儲區的,是以如果在靜态方法裡使用了靜态變量,這就會有線程安全的問題!

此問題,為我在實際項目中的發現,問題的描述以及解決可能沒有那麼準确,如有發現不對的地方,歡迎大家進行指正,謝謝!