委派用法記錄 (執行緒)

  • 833
  • 0

開發winform的時後,常會遇到一個問題「Additional information: 跨執行緒作業無效: 存取控制項 'textBox1' 時所使用的執行緒與建立控制項的執行緒不同。」

上述簡單的一段話,卻包含了許多需要理解的東西才能得到解決的辦法,當然直接去找高手們的文章便可解決,但不瞭解原由以後仍會不知所措。

 

首先用簡單的範例模擬這個問題發生

        private void button9_Click(object sender, EventArgs e)
        {
            textBox1.Text = "";
            //在主執行緒上對文字方塊印出個A
            textBox1.Text += "A:" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;

            var ts = Task.Factory.StartNew(() =>
            {
                //在副執行緒上對文字方塊印出個B
                var thisthread = Thread.CurrentThread.ManagedThreadId;
                textBox1.Text += "B(" + thisthread + "):" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
            });

            //在主執行緒上對文字方塊印出個C
            textBox1.Text += "C:" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
        }

很快的就會看到紅紅的錯誤,出錯的原因講的直白點是 - A和C都在主執行緒上跑,現在B你要從其他的執行緒來,我不答應。 (大概是將)

解法在網路上也很多,但正確的解法是透過委派(Delegate)的方式:使B能透過一個調解者(Delegate)進來主執行緒,因此調整一下程式。

        //新增一個委派
        private delegate void predicate(string s);
        private void button9_Click(object sender, EventArgs e)
        {
            textBox1.Text = "";
            textBox1.Text += "A:" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;

            //宣告委派
            predicate pd = changetext;
            var ts = Task.Factory.StartNew(() =>
            {
                //此副執行緒的id,可用來識別是否和主執行緒不同
                var tid = Thread.CurrentThread.ManagedThreadId;
                Thread.Sleep(1000);
                //透過invoke執行被委派的方法
                this.Invoke(pd, new object[] { "B(" + tid + ")" });

            });

            textBox1.Text += "C:" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
        }


        private void changetext(string s)
        {
            textBox1.Text += s + ":" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
        }

此時會看到的結果如下:

A:10
C:10
B(12):10

括號內的就是副執行緒的id,冒號後的為主執行緒的id,透過委派的方法,可以使B透過主執緒來進行動作。這樣就解決了跨不同執行緒的問題

接著透過action委派的方法,調整一下委派調用,可以少寫一些code:

        private void button9_Click(object sender, EventArgs e)
        {
            textBox1.Text = "";
            textBox1.Text += "A:" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;

            var ts = Task.Factory.StartNew(() =>
            {
                var tid = Thread.CurrentThread.ManagedThreadId;
                Thread.Sleep(5000);
                //無傳回值的委派,無須像delegate一樣需要聲明
                this.Invoke(new Action(() =>
                {
                    textBox1.Text += "B(" + tid + "):" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
                }));

            });
            textBox1.Text += "C:" + Thread.CurrentThread.ManagedThreadId + Environment.NewLine;
        }

講的很粗略如有不對之處再請大大們不吝和我說。