一:問題
以前有被面試官問過,在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.csui
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 }
執行效果 this