多執行緒中容易被忽略的介面假死的問題
阿新 • • 發佈:2019-02-18
介面假死的問題這兩天一直困擾著我,對著自己的程式碼一遍一遍 的在尋找,但是始終不得其原因,後來再深入的到每個呼叫方法的時候,終於發現了一個細節沒有關注上,這個就是導致我的介面卡死的問題的關鍵。
如下做個這個問題真相的還原!!
1、
2、//輸出日誌通知類資訊 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); }
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、注意:靜態變數,由於是在類載入時佔用一個儲存區,每個執行緒都是共用這個儲存區的,所以如果在靜態方法裡使用了靜態變數,這就會有執行緒安全的問題!
此問題,為我在實際專案中的發現,問題的描述以及解決可能沒有那麼準確,如有發現不對的地方,歡迎大家進行指正,謝謝!