iT邦幫忙

0

[WinForm][C#]為什麼跨執行緒作業可以get控制項,不能set控制項

  • 分享至 

  • xImage

在實作時,發現這個現象,請教各位大大,這當中的原理.
謝謝~

set控制項

private System.Timers.Timer _TimersTimer;


private void Timer22_f_Load(object sender, EventArgs e)
{
    this._TimersTimer = new System.Timers.Timer();
    this._TimersTimer.Interval = 100;
    this._TimersTimer.Elapsed += new System.Timers.ElapsedEventHandler(_TimersTimer_Elapsed); 
    this._TimersTimer.Start();
} 

void _TimersTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{ 
    label1.Text = (int.Parse(label1.Text) + 1).ToString(); 
}
   

出現錯誤訊息 : 跨執行緒作業無效: 存取控制項 'label1' 時所使用的執行緒與建立控制項的執行緒不同。

但如果不要存,只有取的話,卻可以.
為什麼會這樣呢?

    void _TimersTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    { 
        int iTest = (int.Parse(label1.Text) + 1); 
        Console.WriteLine(iTest);
    }
看更多先前的討論...收起先前的討論...
weiclin iT邦高手 4 級 ‧ 2018-01-02 17:14:19 檢舉
想想一個可能性: 如果兩個執行緒同時 set 怎麼辦?
圓頭人 iT邦研究生 5 級 ‧ 2018-01-03 08:47:46 檢舉
聽起來要有lock機制,才能避免被同時改的問題.
但如果只是訪問機制,沒有lock,也不能解決這個問題,不太明白訪問機制的存在用意是?
還是說這個訪問機制就是要配合lock使用,不能單獨使用呢?
或是有什麼情況下可以單獨使用,不需要用到lock呢?
delegate void UpdateControl(Control Ctrl, string Msg);
private object _objLock = new object();
void _mUpdateControl(Control Ctrl, string Msg)
{
if (Ctrl is Label)
((Label)Ctrl).Text = Msg;
}

# lock
delegate void UpdateControl(Control Ctrl, string Msg);
private object _objLock = new object();
void _mUpdateControl(Control Ctrl, string Msg)
{
lock (this._objLock)
{
if (Ctrl is Label)
((Label)Ctrl).Text = Msg;
}
}
weiclin iT邦高手 4 級 ‧ 2018-01-03 12:40:27 檢舉
這麼說吧, 因為允許多個 thread set 會產生很不穩定的程式, 所以近來的趨勢是語言會阻止你這麼做, 並強迫你使用 thread safe 的方法去執行, 可以參考: https://stackoverflow.com/questions/661561/how-do-i-update-the-gui-from-another-thread-in-c
圓頭人 iT邦研究生 5 級 ‧ 2018-01-04 08:41:27 檢舉
原來如此...
因為這樣會產生不穩定程式,所以大家想辦法弄出又可以thread set又穩定的程式
所以就有了這些寫法,甚至語言框架直接檢查不讓我們寫出這樣危險的東西.
謝謝大大~
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 個回答

0
JamesDoge
iT邦高手 1 級 ‧ 2023-02-04 13:48:19

因為 System.Timers.Timer 的 Elapsed 事件是在非 UI 執行緒上觸發的。
因此,您不能直接存取或更新 UI 控制項,否則將產生錯誤。

解決方法是將存取控制項的代碼放在 Control.Invoke 或 Control.BeginInvoke 方法中,這些方法會將程式碼包裹在 UI 執行緒上,以避免跨執行緒錯誤。

void _TimersTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{ 
    label1.Invoke((MethodInvoker)delegate {
        label1.Text = (int.Parse(label1.Text) + 1).ToString(); 
    });
}

參考
https://learn.microsoft.com/zh-tw/dotnet/api/system.windows.forms.control.invoke?view=windowsdesktop-7.0

我要發表回答

立即登入回答