WinForm中線程的處理(UI線程)

單線程的winfom程序中,設置一個控件的值是很easy的事情,直接 this.TextBox1.value = "Hello World!";就搞定了,但是如果在一個新線程中這麼做,比如:

複製代碼
private void btnSet_Click(object sender, EventArgs e)
{    
    Thread t 
= new Thread(new ParameterizedThreadStart(SetTextBoxValue));
    
//當然也可以用匿名委託寫成Thread t = new Thread(SetTextBoxValue);
    t.Start("Hello World");
}

void SetTextBoxValue(object obj) 
{
    
this.textBox1.Text = obj.ToString();
}
複製代碼

 運行時,會報出一個無情的錯誤:
線程間操作無效: 從不是創建控件“textBox1”的線程訪問它。

究其原因,winform中的UI控件不是線程安全的,如果可以隨意在任何線程中改變其值,你創建一個線程,我創建一個線程,大家都來搶着更改"TextBox1"的值,沒有任何秩序的話,天下大亂...

解決辦法:
1.掩耳盜鈴法(Control.CheckForIllegalCrossThreadCalls = false;)--僅Winform有效

複製代碼
using System;
using System.Threading;
using System.Windows.Forms;

namespace ThreadTest
{
    
public partial class Form1 : Form
    {        

        
public Form1()
        {
            InitializeComponent();
            
Control.CheckForIllegalCrossThreadCalls = false;//這一行是關鍵      
        }
       

        
private void btnSet_Click(object sender, EventArgs e)
        {           
            Thread t 
= new Thread(new ParameterizedThreadStart(SetTextBoxValue));
            t.Start(
"Hello World");
        }

        
void SetTextBoxValue(object obj) 
        {
            
this.textBox1.Text = obj.ToString();
        }        
    }
}
複製代碼

設置Control.CheckForIllegalCrossThreadCalls爲false,相當於不檢測線程之間的衝突,允許各路線程隨便亂搞,當然最終TextBox1的值到底是啥難以預料,只有天知道,不過這也是最省力的辦法

2.利用委託調用--最常見的辦法(僅WinForm有效)

複製代碼
using System;
using System.Threading;
using System.Windows.Forms;

namespace ThreadTest
{
    
public partial class Form1 : Form
    {
        
delegate void D(object obj);

        
public Form1()
        {
            InitializeComponent();            
        }
       

        
private void btnSet_Click(object sender, EventArgs e)
        {           
            Thread t 
= new Thread(new ParameterizedThreadStart(SetTextBoxValue));
            t.Start(
"Hello World");
        }

        
void SetTextBoxValue(object obj) 
        {
            if (textBox1.InvokeRequired)
            {
                D d = new D(DelegateSetValue);
                textBox1.Invoke(d,obj);

            }

            
else 
            {
                
this.textBox1.Text = obj.ToString();
            }
        }

        
void DelegateSetValue(object obj) 
        {
            
this.textBox1.Text = obj.ToString();
        }
    }
}
複製代碼

3.利用SynchronizationContext上下文 -- 最神祕的方法(Winform/Silverlight能用)

之所以說它神祕,是因爲msdn官方對它的解釋據說也是不清不楚 

複製代碼
using System;
using System.Threading;
using System.Windows.Forms;

namespace ThreadTest
{
    
public partial class Form1 : Form
    {
        
public Form1()
        {
            InitializeComponent();            
        }       

        
private void btnSet_Click(object sender, EventArgs e)
        {
            Thread t 
= new Thread(new ParameterizedThreadStart(Run));
            MyPram _p 
= new MyPram() { context = SynchronizationContext.Current, parm = "Hello World" };
            t.Start(_p);
        }

        
void Run(object obj) 
        {
            MyPram p 
= obj as MyPram;
            
p.context.Post(SetTextValue, p.parm);
        }

        
void SetTextValue(object obj) 
        {
            
this.textBox1.Text = obj.ToString();
        }
    }

    
public class MyPram 
    {
        
public SynchronizationContext context { setget; }
        
public object parm { setget; }
    }
}
複製代碼

4.利用BackgroundWorker --最偷懶的辦法(Winform/Silverlight通用)

BackgroundWorker會在主線程之外,另開一個後臺線程,我們可以把一些處理放在後臺線程中處理,完成之後,後臺線程會把結果傳遞給主線程,同時結束自己。

複製代碼
using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace ThreadTest
{
    
public partial class Form1 : Form
    {
        
public Form1()
        {
            InitializeComponent();            
        }       

        
private void btnSet_Click(object sender, EventArgs e)
        {
            
//MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());
            using (BackgroundWorker bw = new BackgroundWorker())
            {
                bw.RunWorkerCompleted 
+= new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
                bw.DoWork 
+= new DoWorkEventHandler(bw_DoWork);
                bw.RunWorkerAsync(
"Hello World");
            }
        }

        
void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            
//MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());
            e.Result = e.Argument;//這裏只是簡單的把參數當做結果返回,當然您也可以在這裏做複雜的處理後,再返回自己想要的結果(這裏的操作是在另一個線程上完成的)
        }

        
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            
//這時後臺線程已經完成,並返回了主線程,所以可以直接使用UI控件了
            this.textBox1.Text = e.Result.ToString();
            
//MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString());
        }       
    }    
}
複製代碼

5.Dispatcher.BeginInvoke--Silverlight的獨門祕籍 

複製代碼
代碼
using System.Threading;
using System.Windows.Controls;
using System.Windows.Input;

namespace ThreadTest
{
    
public partial class MainPage : UserControl
    {
        
public MainPage()
        {
            InitializeComponent();
        }

        
private void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Thread t 
= new Thread(SetTextValue);
            t.Start(
"Hello World");
        }

        
void SetTextValue(object text) 
        {
            
this.Dispatcher.BeginInvoke(() => { this.txt.Text = text.ToString(); });            
        }
    }
}
複製代碼
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章