在C# 的應用程式開發中, 我們經常要把UI執行緒和作業執行緒分開,防止界面停止回應。 同時我們又需要在作業執行緒中更新UI界面上的控制元件,
下面介紹幾種常用的方法
執行緒間操作無效
界面上有一個button和一個label, 點擊button會啟動一個執行緒來更新Label的值
private void button1_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel));
thread1.Start("更新Label");
}
private void UpdateLabel(object str)
{
this.label1.Text = str.ToString();
}
運行后, 程式會報錯 "跨執行緒操作無效,從不是創建"label1"的執行緒訪問它"
這是因為.NET禁止了跨執行緒呼叫控制元件, 否則誰都可以操作控制元件,最后可能造成錯誤。
下面介紹幾種跨執行緒呼叫控制元件的方法
第一種辦法:禁止編譯器對跨執行緒訪問做檢查這是最簡單的辦法, 相當于不檢查執行緒之間的沖突,允許各個執行緒隨便亂搞,最后Lable1控制元件的值是什么就難以預料了 (不推薦使用這種方法)
public Form1()
{
InitializeComponent();
// 加入這行
Control.CheckForIllegalCrossThreadCalls = false;
}
第二種辦法: 使用delegate和invoke來從其他執行緒中呼叫控制元件呼叫控制元件的invoke方法,就可以控制控制元件了,例如
private void button2_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel2));
thread1.Start("更新Label");
}
private void UpdateLabel2(object str)
{
if (label2.InvokeRequired)
{
// 當一個控制元件的InvokeRequired屬性值為真時,說明有一個創建它以外的執行緒想訪問它
Action<string> actionDelegate = (x) => { this.label2.Text = x.ToString(); };
// 或者
// Action<string> actionDelegate = delegate(string txt) { this.label2.Text = txt; };
this.label2.Invoke(actionDelegate, str);
}
else
{
this.label2.Text = str.ToString();
}
}
第三種辦法: 使用delegate和BeginInvoke來從其他執行緒中控制控制元件只要把上面的 this.label2.Invoke(actionDelegate, str); 中的 Invoke 改為BeginInvoke方法就可以了
Invoke方法和BeginInvoke方法的區別是
Invoke方法是同步的, 它會等待作業執行緒完成,
BeginInvoke方法是異步的, 它會另起一個執行緒去完成作業執行緒
第四種辦法: 使用BackgroundWorker組件(推薦使用這個方法)BackgroundWorker是.NET里面用來執行多執行緒任務的控制元件,它允許編程者在一個單獨的執行緒上執行一些操作。耗時的操作(如下載和資料庫事務)。用法簡單
private void button4_Click(object sender, EventArgs e)
{
using (BackgroundWorker bw = new BackgroundWorker())
{
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync("Tank");
}
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// 這里是后臺執行緒, 是在另一個執行緒上完成的
// 這里是真正做事的作業執行緒
// 可以在這里做一些費時的,復雜的操作
Thread.Sleep(5000);
e.Result = e.Argument + "作業執行緒完成";
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//這時后臺執行緒已經完成,并回傳了主執行緒,所以可以直接使用UI控制元件了
this.label4.Text = e.Result.ToString();
}
轉摘自http://www.cnblogs.com/TankXiao/p/3348292.html
轉摘自https://www.51halcon.com/forum.php?mod=viewthread&tid=450&extra=page%3D2%26filter%3Dtypeid%26typeid%3D68
uj5u.com熱心網友回復:
學習了。。
uj5u.com熱心網友回復:
感謝分享。
uj5u.com熱心網友回復:
鼓勵。還可以有其他方法,這里提示兩種,試試?
1、SynchronizationContext (WindowsFormsSynchronizationContext)
2、async await (await支持背景關系捕獲)。
uj5u.com熱心網友回復:
除了第一種不算什么方法。掌握多種方法和一種沒有什么區別。
uj5u.com熱心網友回復:
只有個別的屬性才會報錯uj5u.com熱心網友回復:
加個wpf的
private void xxxxxx()
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate ()
{
xxxxxx
});
}
uj5u.com熱心網友回復:
多謝大佬不過如果用 WPF 的 Dispatcher.Invoke 小心鎖主執行緒,需要構造出主執行緒等待后臺執行緒,而這個后臺執行緒呼叫了 Dispatcher.Invoke 就可以。因為呼叫 Dispatcher.Invoke 將會等待主執行緒呼叫完成,但是此時的主執行緒在等待后臺執行緒,此時兩個執行緒就會相互等待
uj5u.com熱心網友回復:
是的,在使用需加sleep,否則掛了
uj5u.com熱心網友回復:
沒想到過了幾年,csdn還在糾結執行緒。希望大多數人都習慣用 Task 編程,放棄直接 Thread 編程。這樣就不會糾結阻塞、休眠等等說法,希望提高異步多執行緒水平。
uj5u.com熱心網友回復:
其實換 InvokeAsync 就可以解決
uj5u.com熱心網友回復:
沒想到過了幾年,csdn還在糾結執行緒。
希望大多數人都習慣用 Task 編程,放棄直接 Thread 編程。這樣就不會糾結阻塞、休眠等等說法,希望提高異步多執行緒水平。
我寫了一個非常復雜的控制系統(大約20個執行緒共同競爭資源,得自己控制),底層(傳輸指令)是前人寫的,只能用framework 2.0開發
uj5u.com熱心網友回復:
沒想到過了幾年,csdn還在糾結執行緒。
希望大多數人都習慣用 Task 編程,放棄直接 Thread 編程。這樣就不會糾結阻塞、休眠等等說法,希望提高異步多執行緒水平。
設定的是兼容模式2.0 4.0,老是感覺不放心
uj5u.com熱心網友回復:
不要再重復10年前的東西了,不鼓勵這種分享。從來不建議打著分享的名義兜售過期食物,對么一切的核心在SynchronizationContext ,只有一個人提。
OK,到此為止。過期食品就讓他自然過期把
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/48041.html
標籤:C#
