C#新開一個線程取到數據,如何更新到主線程UI上面


   一:問題
  
之前有被面試官問過,在WinForm中,要去網絡上獲取數據,由於網絡環境等原因,不能很快的完成,因此會發生進程阻塞,造成主進程假死的現象,需要怎麼解決?

   二:思路
  
因此,往往是新建一個線程,讓他執行耗時的操作,主線程管理用戶界面,不會出現UI假死的情況,但是通過線程獲取到的數據如何更新回主進程的UI上呢?這是另外一個問題

   三:如下例子
  
我們發現如果直接在線程裏更新UI會報錯,報“從不是創建控件lable1的線程訪問它”,爲什麼會報這個錯呢?這個問題就是跨線程訪問控件問題,窗體上的控件只允許創建它們的線程訪問,也就是主線程(UI線程),如果非主線程訪問則會發生異常我們看到會報錯,且這個錯誤是“從不是創建控件lable1的線程訪問它”

 
這裏寫圖片描述

TestClass.cs

 

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6 using System.Threading.Tasks;
 7 
 8 namespace WindowsFormsApplication1
 9 {
10     public class TestClass
11     {
12         //聲明一個delegate(委託)類型:TestDelegate,該類型可以搭載返回值爲空,參數只有一個(long型)的方法。 
13         public delegate void TestDelegate(long i);
14 
15         //聲明一個TestDelegate類型的對象。該對象代表了返回值爲空,參數只有一個(long型)的方法。它可以搭載N個方法。 
16         public TestDelegate mainThread;
17 
18         /// 測試方法 
19         /// <summary>          
20         /// </summary> 
21         public void TestFunction()
22         {
23             long i = 0;
24             while (true)
25             {
26                 i++;
27                 mainThread(i); //調用委託對象 
28                 Thread.Sleep(1000);  //線程等待1000毫秒 
29             }
30         }
31     }
32 }

 

Program.cs

 1 using System;
 2 using System.Collections.Generic;
 3 using System.ComponentModel;
 4 using System.Data;
 5 using System.Drawing;
 6 using System.Linq;
 7 using System.Text;
 8 using System.Threading;
 9 using System.Threading.Tasks;
10 using System.Windows.Forms;
11 
12 namespace WindowsFormsApplication1
13 {
14     public partial class Form1 : Form
15     {
16         public Form1()
17         {
18             InitializeComponent();
19         }
20 
21         private void button1_Click(object sender, EventArgs e)
22         {
23             //創建TestClass類的對象 
24             TestClass testClass = new TestClass();
25 
26             //在testclass對象的mainThread(委託)對象上搭載兩個方法,在線程中調用mainThread對象時相當於調用了這兩個方法。 
27             testClass.mainThread = new TestClass.TestDelegate(RefreshLabMessage1);
28             testClass.mainThread += new TestClass.TestDelegate(RefreshLabMessage2);
29 
30             //創建一個無參數的線程,這個線程執行TestClass類中的TestFunction方法。 
31             Thread testClassThread = new Thread(new ThreadStart(testClass.TestFunction));
32             //啓動線程,啓動之後線程纔開始執行 
33             testClassThread.Start();
34         }
35 
36         /// <summary> 
37         /// 在界面上更新線程執行次數 
38         /// </summary> 
39         /// <param name="i"></param> 
40         private void RefreshLabMessage1(long i)
41         {
42             //判斷該方法是否被主線程調用,也就是創建labMessage1控件的線程,當控件的InvokeRequired屬性爲ture時,說明是被主線程以外的線程調用。如果不加判斷,會造成異常 
43             if (this.labMessage1.InvokeRequired)
44             {
45                 //再次創建一個TestClass類的對象 
46                 TestClass testclass = new TestClass();
47                 //爲新對象的mainThread對象搭載方法 
48                 testclass.mainThread = new TestClass.TestDelegate(RefreshLabMessage1);
49                 //this指窗體,在這調用窗體的Invoke方法,也就是用窗體的創建線程來執行mainThread對象委託的方法,再加上需要的參數(i) 
50                 this.Invoke(testclass.mainThread, new object[] { i });
51             }
52             else
53             {
54                 labMessage1.Text = i.ToString();
55             }
56         }
57 
58 
59         /// <summary> 
60         /// 在界面上更新線程執行次數 
61         /// </summary> 
62         /// <param name="i"></param> 
63         private void RefreshLabMessage2(long i)
64         {
65             //同上 
66             if (this.labMessage2.InvokeRequired)
67             {
68                 //再次創建一個TestClass類的對象 
69                 TestClass testclass = new TestClass();
70                 //爲新對象的mainThread對象搭載方法 
71                 testclass.mainThread = new TestClass.TestDelegate(RefreshLabMessage2);
72                 //this指窗體,在這調用窗體的Invoke方法,也就是用窗體的創建線程來執行mainThread對象委託的方法,再加上需要的參數(i) 
73                 this.Invoke(testclass.mainThread, new object[] { i });
74             }
75             else
76             {
77                 labMessage2.Text = i.ToString();
78             }
79         }
80     }
81 }

執行效果 

這裏寫圖片描述 
這裏寫圖片描述

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章