[C#] 控制項跨執行緒作業無效解決方法

  • 38808
  • 0

通常在開發winForm程式時,最容易碰到執行緒(thread)問題,而控制項(controls)的跨續問題也十分讓人頭痛,而通常出現的錯誤訊息,莫過於「跨執行緒作業無效: 存取控制項 ... 時所使用的執行緒與建立控制項的執行緒不同」。

通常在開發winForm程式時,最容易碰到執行緒(thread)問題,而控制項(controls)的跨續問題也十分讓人頭痛,而通常出現的錯誤訊息,莫過於「跨執行緒作業無效: 存取控制項 ... 時所使用的執行緒與建立控制項的執行緒不同」。

因為控制項(control)主要是存在UI Thread(通常是Main Thread)內,所以在非相同執行緒下要更新UI就會產生跨執行緒的錯誤。而一般常見的解法是用delegate搭配Invoke,缺點是不夠彈性,並且有時可能會不如預期的還是會發生跨執行序錯誤。google關鍵字「C# dalegate invoke」會找到很多類似例子。

然後我們從MSDN中 How to: Make Thread-Safe Calls to Windows Forms Controls 這篇文章中可以看到很多相關寫法,其中強調是thread-safe寫法的關鍵字在Controls.InvokeRequired

MSDN上對這個值的說明「這個值會指示是否由於呼叫端是在建立控制項之執行緒以外的執行緒,因此在進行控制項的方法呼叫時,應呼叫叫用 (Invoke) 方法。

而為了更有效便利的使用InvokeInvokeRequired去執行跨執行緒控制項操作,我通常會把這個方法撰寫class的擴充方法,並且加入MethodInvoker 委派/lambda的應用。

使用方法與程式碼如下

public class main
{
    //參考用controls
    TextBox tb = new TextBox();

    void Func()
    {
        //使用lambda
        tb.InvokeIfRequired(() =>
        {
            tb.Text = "hello";
        });

        //使用Action
        tb.InvokeIfRequired(helloAction);
    }

    void helloAction()
    {
        tb.Text = "hello";
    }
}

//擴充方法
public static class Extension
{
    //非同步委派更新UI
    public static void InvokeIfRequired(
        this Control control, MethodInvoker action)
    {
        if (control.InvokeRequired)//在非當前執行緒內 使用委派
        {
            control.Invoke(action);
        }
        else
        {
            action();
        }
    }
}

 

MSDN參考資料:

How to: Make Thread-Safe Calls to Windows Forms Controls
https://msdn.microsoft.com/en-us/library/ms171728%28v=vs.80%29.aspx

Control.InvokeRequired 屬性
https://msdn.microsoft.com/zh-tw/library/system.windows.forms.control.invokerequired%28v=vs.110%29.aspx

Lambda 運算式
https://msdn.microsoft.com/zh-tw/library/vstudio/bb397687%28v=vs.110%29.aspx

MethodInvoker 委派
https://msdn.microsoft.com/zh-tw/library/system.windows.forms.methodinvoker%28v=vs.110%29.aspx