2016-10-20 34 views
1

我讀過,你不能操縱在不同線程中的控件。Winform控件和線程

我有兩個控件使用相同的Singleton類。這兩個控件在不同的線程中,這就是爲什麼我使用單例來進行通信。

我的問題是: 它只是Winforms的東西,不能跨線程操縱?我可以每次都通過Singleton傳遞數據而不會失敗,還是我必須以其他方式執行此操作?

編輯:

由於我得到的意見,我會更好地澄清,我不是在尋找如何做到這一點,但是當我需要做的是,當我不知道。

+5

「這兩個控件在不同的線程中」...讓我們從這裏開始。你不應該在單獨的線程上有控制。 UI組件應該只存在於一個線程上:主UI線程。如果你想從一個單獨的線程操作主線程上的控件,可以通過控件的Invoke方法來實現。這將安全地安排在事件循環接近時在控件上執行的操作。 –

+0

我不能控制這些控件是如何實現的,它們是CRM框架的獨立插件,這就是它們如何完成的。我知道調用的東西,我的問題更多的是當我需要使用它,當我不在這種情況下。 – Graham

+0

@Graham你不需要控制它們是如何實現的。在一個UI線程上創建它們,並且只能訪問它們或從UI線程操縱它們。 – Servy

回答

5

Winforms控件只屬於非線程安全的非常大的一組.NET Framework類。像List<T>這樣基本的東西不是線程安全的。在List的轉換中,您可以使用關鍵字lock來確保只有一個線程同時訪問成員。

但是,這不能用於Winforms控件,您不能在也訪問該控件的操作系統代碼中注入lock。因此,您的代碼總是在創建控件的同一個線程上運行,這非常困難。這是Application.Run()存在的原因,它是producer-consumer problem的通用解決方案。在操作系統和其他進程中產生多個線程並且您的單個UI線程消耗,從而保持UI對象線程安全。

在技術上可能有多個UI線程,每個線程都有自己的頂級窗口(窗體,而不是子控件)。然而,這也是一個相當危險的情況,SystemEvents.ThemeChanged事件是一個重大的麻煩製造者。工具箱中的許多控件都訂閱該事件,當活動的Windows主題發生更改時,他們使用它來重繪自己。此事件在單個線程上引發,通常是您創建的第一個UI線程。

這意味着第二個線程上的控件將在錯誤的線程上獲取此事件。這很容易造成僵局。當工作站被鎖定時(Win + L鍵),通常切換到安全桌面並返回觸發ThemeChanged事件。調試過於醜陋,看起來like this,幾乎無法修復。如果你這樣做,那麼你幾乎不得不創建自己的控件類而不使用工具箱。

「不要這樣做」是唯一的好建議,它從來沒有必要。

0

如果您有一個線程,您想要修改控件的位置,應該調用BeginInvoke控件的方法。例如:

Task.Run(() => 
{ 
    label1.BeginInvoke(new Action(() => 
    { 
     label1.Text = "Something"; 
    })); 
}) 
+0

如果你可以使用'Task.Run',你也可以使用'async/await'和* not *必須使用委託。使用'IProgress '接口也可以更好地報告跨線程的進程,而不需要將硬編碼控制轉換爲後臺代碼 –