Thread 執行緒(線程)
當點擊按鈕時,會去做很複雜的事(跑一萬次) 調試 > 輸出
不是真死,是假死
單Thread 執行緒的問題
模擬一個很複雜的方法,造成視窗卡死
當點擊按鈕時,會去做很複雜的事(跑一萬次)
調試 > 輸出
1
2
3
4
5
6
7
8
private void button1_Click(object sender, EventArgs e) {
Test(); //模擬一個很複雜的方法(跑一萬次)
}
void Test() {
for (int i = 0; i < 10000; i++) {
Console.WriteLine(i);
}
}
造成視窗卡死(不是真死,是假死),這就是單線程的問題。
在vs當中,每運行一個程序,cpu都會分配一個主線程來做,
我們能夠看得到操作視窗、移動、點擊…,都是主線程來做的,
當我們點擊這個按鈕時,主線程跑去做,就沒有人做原本的事,
就會造成視窗卡死(不是真死,是假死),這就是單線程的問題。
解決這個問題也很簡單,就多開一個線程給他。
解決視窗卡死問題(開新線程)
主線程幫我們去執行這個視窗
而新線程幫我們去執行Test這個方法(很複雜的事)
剛的問題就是,我只有一個線程,但他去做別的事了。 那我就再開一個線程去做複雜的事
讓主線程回歸做自己事
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void button1_Click(object sender, EventArgs e)
{
//創建一個線程去執行這個方法
Thread t = new Thread(Test); //傳入方法名
//標記這個線程準備就緒了,可以隨時執行
//具體什麼時候執行線程
//由cpu決定
t.Start();//strat不是真的開始,只是標記
Test(); //模擬一個很複雜的方法(跑一萬次)
}
void Test() {
for (int i = 0; i < 10000; i++) {
Console.WriteLine(i);
}
}
線程是cpu去決定執行的,我們不能控制決定的。
我們只能告訴cpu,這個線程準備好了,你隨時可以執行。
1
2
Thread t = new Thread(Test); //創建一個線程去執行這個方法
t.Start();//strat不是真的開始,只是標記
但是,這個時候你就會發現,疑~~~
我應用程式都關了,怎麼程序怎麼還在跑(輸出Console)
(主線程都關了,程序還在跑…)
因為執行緒又分為:前景執行緒 和 背景執行緒
默認情況下,只要我們新增的執行緒,都叫做前景執行緒。
所以我們的應用程序必須要等到「前景執行緒」全部都執行完了,我才會徹底的結束掉關閉程序。
而「背景執行緒」是只要前景執行緒都關閉了,背景執行緒就會結束。
所以我們只要把剛新增的執行緒設為「背景執行緒」就可以了。
1
2
//線程設為後台線程
t.IsBackground = true;
前景執行緒 & 背景執行緒
- 前景執行緒(前台線程) 前景執行緒都關閉了,程序才會關閉。
背景執行緒
- 背景執行緒(後台線程) 只要前景執行緒都關閉了,背景執行緒就會馬上「自動關閉」。
默認情況下,只要我們新增的執行緒,都叫做前景執行緒。
所以會發生我關閉了應用程式,但程序卻還在跑(運行)
只要把剛設的thread設為背景執行緒就可以了
設定背景執行緒
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void button1_Click(object sender, EventArgs e)
{
//創建一個線程去執行這個方法
Thread t = new Thread(Test); //傳入方法名
//線程設為後台線程
t.IsBackground = true;
//標記這個線程準備就緒了,可以隨時執行
//具體什麼時候執行線程
//由cpu決定
t.Start();//strat不是真的開始,只是標記, 我們只能告訴cpu這個thread準備好了,你隨時可以執行它
Test(); //模擬一個很複雜的方法(跑一萬次)
}
void Test() {
for (int i = 0; i < 10000; i++) {
Console.WriteLine(i);
}
}
1 2 3 4 Thread t = new Thread(Test); //產生新的thread指向Test方法 t.IsBackground = true; //設為背景thread,只要前景thread全都關閉了,背景thread馬上自動關閉 t.Start(); //這時候thread己經產生,但還沒運行,具體執行時間由CPU決定 Test(); //很複雜耗時的方法
在.net下,是不允許跨執行緒的訪問
報錯:System.InvalidOperationException: ‘跨執行緒作業無效: 存取控制項 ‘textBox1’ 時所使用的執行緒與建立控制項的執行緒不同。’
1
2
3
4
5
6
7
8
9
10
11
12
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(Test); //創建一個線程去執行這個方法(傳入方法名)
t.IsBackground = true;//線程設為後台線程
t.Start();//strat不是真的開始,只是標記這個線程準備就緒了,可以隨時執行,具體執行時間由cpu決定
Test();
}
void Test() {
for (int i = 0; i < 100000; i++) {
textBox1.Text = i.ToString(); //報錯,不允許跨執行緒的訪問
}
}
怎麼辦?
取消跨線程的訪問
取消跨線程的訪問
在應用程式載入時,取消跨線程的訪問
Control.CheckForIllegalCrossThreadCalls = false;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void Form1_Load(object sender, EventArgs e) {
//取消跨線程的訪問
Control.CheckForIllegalCrossThreadCalls = false;
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(Test); //創建一個線程去執行這個方法(傳入方法名)
t.IsBackground = true;//線程設為後台線程
t.Start();//strat不是真的開始,只是標記這個線程準備就緒了,可以隨時執行,具體執行時間由cpu決定
Test();
}
void Test() {
for (int i = 0; i < 100000; i++) {
textBox1.Text = i.ToString(); //報錯,不允許跨執行緒的訪問
}
}
thread.Abort 終止線程
這邊還會有個問題,在關閉應用程式時,有時候會出現異常,
異常不是每次都會出現,為什麼會這樣?
因為關閉應用程式時,
主線程沒了,視窗沒了,資源被釋放了,
但新線程可能某種原因,沒有馬上結束,
他卻還在訪問主線程的textBox1,就拋異常了
這時候就在關閉應用程式時,Abort()線程就可以了
1
2
3
4
5
6
7
8
9
10
11
12
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//異常不是每次都會出現,關閉應用程式時
//主線程沒了,視窗沒了,資源釋放了
//但新線程可能某種原因,沒有馬上結束,
//還在訪問主線程的textBox1就拋異常了
//當你點擊關閉視窗的時候,判斷新線程是否為null
if (t != null) {
t.Abort(); //結束這線程 (已過時)
}
}
要注意的是:線程被abort後,就不能再strat了
1
2
3
4
5
6
7
Thread t = new Thread(Test);
t.IsBackground = true;
t.Start();
//線程被abort後,就不能再strat了
t.Abort();
t.Start(); //報錯
Thread.Sleep
讓線程停止運行3秒後,再執行”Hello”
1
2
Thread.Sleep(3000); //睡3秒
Console.WriteLine("Hello");
完整程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Thread t;
private void button1_Click(object sender, EventArgs e)
{
//創建一個線程去執行這個方法
t = new Thread(Test); //傳入方法名
//線程設為後台線程
t.IsBackground = true;
//標記這個線程準備就緒了,可以隨時執行
//具體什麼時候執行線程
//由cpu決定
t.Start();//strat不是真的開始,只是標記
//線程是cpu去決定執行的,我們不能決定
Test(); //模擬一個很複雜的方法(跑一萬次)
//線程被abort後,就不能再strat了
//t.Abort();
//t.Start(); //報錯
}
void Test() {
for (int i = 0; i < 100000; i++) {
textBox1.Text = i.ToString();
}
}
private void Form1_Load(object sender, EventArgs e)
{
//取消跨線程的訪問
Control.CheckForIllegalCrossThreadCalls = false;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//異常不是每次都會出現,關閉應用程式時
//主線程沒了,視窗沒了,資源釋放了
//但新線程可能某種原因,沒有馬上結束,
//還在訪問主線程的textBox1就拋異常了
//當你點擊關閉視窗的時候,判斷新線程是否為null
if (t != null) {
t.Abort(); //結束這線程 (已過時)
}
}
總結
- 單線程帶來程式假死的問題
- 怎麼解決,用多線程,多開一個線程
- 前台線程:所有的前台線程全部關閉了,這個程序才會關閉
- 後台線程:所有的前台線程全部一關閉了,後台線程自動關閉,然後程序自動關閉
- Thread.Start()啟動線程:是告訴CPU我可以被執行了,具體什麼時候執行,由CPU決定,我們是不能寫程式碼決定的,我們做不到的
- Thread.Abort()終止線程,終止完成之後,不能再Start()
- Thread.Sleep(1) 可以使當前線程停止一段時間再運行(睡一下)