C#多線程訪問Winform控件跨線程問題


我們在做winform應用的時候,大部分情況下都會碰到使用多線程控制界面上控件信息的問題,隨之就極有可能出現這個異常:Cross-thread operation not valid:Control 'textBox1' accessed from a thread other than the thread it was created on。

在網上看了一些解決方案,第一個就是在winfrom的load方法里加入這行:Control.CheckForIllegalCrossThreadCalls =false;(不推薦)

這句代碼就是說在這個類中我們不檢查跨線程的調用是否合法(如果沒有加這句話運行也沒有異常,那么說明系統以及默認的采用了不檢查的方式)。然而,這種方法不可取。我們查看CheckForIllegalCrossThreadCalls 這個屬性的定義,就會發現它是一個static的,也就是說無論我們在項目的什么地方修改了這個值,他就會在全局起作用。而且像這種跨線程訪問是否存在異常,我們通常都會去檢查。如果項目中其他人修改了這個屬性,那么我們的方案就失敗了。
第二個就是所謂的使用delegate和invoke來從其他線程中控制控件信息,但是我不這么認為,感覺這種方法的要點在於Control.Invoke Method,而感覺使用delegate和invoke會會誤導別人,好像使用一個delegate然后invoke就好了,其實delegate只不過是起輔助作用而已,真正起作用的還是Control.Invoke Method,看下面的code:

 

public partial class Form1 : Form
    {
        private delegate void FlushClient();//代理
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            Thread thread = new Thread(CrossThreadFlush);

            thread.IsBackground=true;
            thread.Start();
        }

        private void CrossThreadFlush()
        {
            //將代理綁定到方法 
            FlushClient fc = new FlushClient(ThreadFuntion);
            this.BeginInvoke(fc);
       //重點其實是在這里,前面定義的delegate什么的都是為了讓創建這個Control的線程去調用這個ThreadFuntion 方法         
} private void ThreadFuntion() { while (true) { this.textBox1.Text = DateTime.Now.ToString(); Thread.Sleep(1000); } } }

說到Control.Invoke Method,就不得不提下Control.InvokeRequired Property,借助下面的code來看下這個property:

 f (this.InvokeRequired)
            {
                this.BeginInvoke(new MethodInvoker(LoadGlobalImage));
                return;
            }

c#中禁止跨線程直接訪問控件,InvokeRequired是為了解決這個問題而產生的當一個控件的InvokeRequired屬性值為真時,說明有一個創建它以外的線程想訪問它。此時它將會在內部調用new MethodInvoker(LoadGlobalImage)來完成下面的步驟,這個做法保證了控件的安全,你可以這樣理解,有人想找你借錢,他可以直接在你的錢包中拿,這樣太不安全,因此必須讓別人先要告訴你,你再從自己的錢包把錢拿出來借給別人,這樣就安全了。

這樣就可以很好的理解上面那個winform程序中 this.BeginInvoke(fc);這行code了,這個執行后其實也就是主線程在調用fc中綁定的方法ThreadFuntion()了,這種方式其實相當於把這個新開的線程“注入”到了主控制線程中,它取得了主線程的控制。只要這個線程不返回,那么主線程將永遠都無法響應。就算新開的線程中不使用無限循環,使可以返回了。這種方式的使用多線程也失去了它本來的意義。

下面就要用到InvokeRequired這個propety

public partial class Form1 : Form
    {
        private delegate void FlushClient();//代理
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            Thread thread = new Thread(CrossThreadFlush);
            thread.IsBackground = true;
            thread.Start();
        }

        private void CrossThreadFlush()
        {
            while (true)
            {
                //將sleep和無限循環放在等待異步的外面
                Thread.Sleep(1000);
                ThreadFunction();
            }
        }
        private void ThreadFunction()
        {
            if (this.textBox1.InvokeRequired)//等待異步
            {
                FlushClient fc = new FlushClient(ThreadFunction);
                this.Invoke(fc);//通過代理調用刷新方法
            }
            else
            {
                this.textBox1.Text = DateTime.Now.ToString();
            }
        }
    }

運行上述代碼,我們可以看到問題已經被解決了,通過等待異步,我們就不會總是持有主線程的控制,這樣就可以在不發生跨線程調用異常的情況下完成多線程對winform多線程控件的控制了。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM