C#異步調用與線程總結

委託調用、子線程程調用、與線程池調用
1,委託調用
(1),同步委託:委託的Invoke方法用來進行同步調用。同步調用也可以叫阻塞調用,它將阻塞當前線程,然後執行調用,調用完畢後再繼續向下進行。
 從下面的例子中可以看到,同步委託的執行是在主線程main中執行的,所以當執行委託時,當前工作會處於等待狀態,開始執行委託,當委託執行完後在繼續執行“當前工作”

public delegate int AddHandler(int i,int y); private void button1_Click(object sender, EventArgs e) { //添加當前主線程名稱“main” Thread.CurrentThread.Name = "main"; AddHandler handler = new AddHandler(Add); Debug.WriteLine(handler.Invoke(1,2)); Debug.WriteLine("OK"); } int Add(int x,int y) { //輸出當前執行操作的現場 Debug.WriteLine(Thread.CurrentThread.Name); return x + y; }
輸出結果:main
               OK
 從Debug.WriteLine(Thread.CurrentThread.Name) 看出同步委託代碼執行所在的線程與調用方式相關,同步委託代碼執行所在的線程等於調用委託所在的線程.
(2),異步委託:異步調用不阻塞主線程,而是把調用在線程池中的新線程中執行,我們可以不必關心,也無需關心這個“新線程”是怎麼定義的
     委託的異步調用通過BeginInvoke和EndInvoke來實現。
 從下面的例子中可以看到,異步委託的執行是在新線程中執行,所以當執行委託時,當前工作會不會阻塞,異步委託 與當前線程是同時執行的。        
public delegate int AddHandler(int i,int y); private void button1_Click(object sender, EventArgs e) { //添加當前主線程名稱“main” Thread.CurrentThread.Name = "main"; AddHandler handler = new AddHandler(Add); Debug.WriteLine(handler.BeginInvoke(1, 2, null, null)); Debug.WriteLine("OK"); } int Add(int x,int y) { //輸出當前執行操作的現場 Debug.WriteLine(Thread.CurrentThread.Name); return x + y; }
輸出結果:OK
               空   
 Debug.WriteLine(Thread.CurrentThread.Name) 輸出爲空,看出異步委託代碼執行是在我們沒有指定名字的新線程中執行的。
 備註:由於異步委託時啓用線程池線程執行,.Net沒有賦予程序員直接停止其調用的方法,使得我們沒有辦法直接控制委託的停止和執行,假設 Add是一個0-100循環,一般情況下我們是沒有辦法 在委託循環到50讓委託停下來的,二般情況是可以通過一些特殊的手段的需要的話就Goolge一下吧!所以顯得委託調用不夠靈活

下面給一個一步委託返回值的列子:

public delegate int AddHandler(int i,int y); private void button1_Click(object sender, EventArgs e) { //添加當前主線程名稱“main” Thread.CurrentThread.Name = "main"; AddHandler handler = new AddHandler(Add); IAsyncResult obj = handler.BeginInvoke(1, 2, null, null); //使用EndInvoke方法接收返回值 int i = handler.EndInvoke(obj); Debug.WriteLine(i.ToString()); } int Add(int x,int y) { //輸出當前執行操作的現場 Debug.WriteLine(Thread.CurrentThread.Name); return x + y; }

2,子線程調用:子線程的最大特點是在子線程執行任務時候,不佔用主線程,而且我們可以自由控制它。Visual C#中使用的線程都是通過自命名空間System.Threading中的Thread類經常實例化完成的。通過Thread類的構造函數來創建可供Visual C#使用的線程,通過Thread中的方法和屬性來設定線程屬性和控制線程的狀態。以下Thread類中的最典型的構造函數語法,在Visual C#中一般使用這個構造函數來創建、初始化Thread實例。        

private void button1_Click(object sender, EventArgs e) { //添加當前主線程名稱“main” Thread.CurrentThread.Name = "main"; //通過Thread類的構造函數線程,並指示一個委託讓線程 執行指定方法 Thread t = new Thread(new ThreadStart(Add)); t.Name = "子線程"; //開始新線程 t.Start(); Debug.WriteLine(Thread.CurrentThread.Name); } void Add() { for (int i = 0; i < 100000; i++) { //輸出當前執行操作的線程名 Debug.WriteLine(Thread.CurrentThread.Name+i); } }

        輸出結果:
        main
        子線程0
        子線程2
        子線程3
        從輸出結果我們 可以看到 ,新線程的執行,不會阻塞主線程。 我們可以通過 Abort()方法結束線程。這裏就不給出代碼了。
        下面說一下,帶參數的線程委託,看下面的代碼:  

void Add(int q) { for (int i = 0; i < q; i++) { //輸出當前執行操作的線程名 Debug.WriteLine(Thread.CurrentThread.Name + i); } } private void button1_Click(object sender, EventArgs e) { Thread.CurrentThread.Name = "main"; Thread t = new Thread(new ThreadStart(Add(100))); t.Name = "子線程"; t.Start(); Debug.WriteLine(Thread.CurrentThread.Name); }
    如果你像這樣 Thread t = new Thread(Add(100));傳入參數的話肯定是不可能的 因爲委託時不能帶參數的,這裏提供一種簡單的解決方法,就是在線程委託中再委託的辦法實現
private void button1_Click(object sender, EventArgs e) { //添加當前主線程名稱“main” Thread.CurrentThread.Name = "main"; //在線程委託中再定義一個委託在新委託中調用方法void Add(int q)。 Thread t = new Thread(new ThreadStart(delegate {Add(1000); })); t.Name = "子線程"; //開始新線程 t.Start(); Debug.WriteLine(Thread.CurrentThread.Name); }

帶返回值的:

private void button1_Click(object sender, EventArgs e) { //添加當前主線程名稱“main” Thread.CurrentThread.Name = "main"; //定義一個變量準備接收子線程返回值 int iResult = 0; //在線程委託中再定義一個委託在新委託中調用方法void Add(int q)。 Thread t = new Thread(new ThreadStart(delegate {iResult = Add(1000); })); t.Name = "子線程"; //開始新線程 t.Start(); //設置一個循環來等待子線程結束 while (t.ThreadState != System.Threading.ThreadState.Stopped) { t.Join(10); } Debug.WriteLine(iResult.ToString()); Debug.WriteLine(Thread.CurrentThread.Name); }

3,線程池調用 :“線程池”是可以用來在後臺執行多個任務的線程集合。這使主線程可以自由地異步執行其他任務。
線程池通常用於服務器應用程序。每個傳入請求都將分配給線程池中的一個線程,因此可以異步處理請求,而不會佔用主線程,也不會延遲後續請求的處理。
ThreadPool(線程池)是一個靜態類,它沒有定義任何的構造方法(),我們只能夠使用它的靜態方法,這是因爲,這是因爲ThreadPool是託管線程池,是由CLR管理的。
ThreadPool使用WaitCallback委託,它所要做的工作是在後臺進行的。使工作項的排隊和運行更容易,可以給工作者線程傳遞一個狀態對象(提供數據)。狀態對象是私有的作用域位於線程層,所以不需要進行同步。
ThreadPool目標是爲了減除線程的初始化開銷,實現並行處理。
一個ThreadPool裏面註冊的線程擁有默認的堆棧大小,默認的優先級。並且,他們都存在於多線程空間(Multithreaded apartment)中。

ThreadPool中的Thread不能手動取消,也不用手動開始。所以ThreadPool並不適用比較長的線程。你要做的只是把一個WaitCallback委託塞給ThreadPool,然後剩下的工作將由系統自動完成。系統會在ThreadPool的線程隊列中一一啓動線程。

當線程池滿時,多餘的線程會在隊列裏排隊,當線程池空閒時,系統自動掉入排隊的線程,以保持系統利用率。

我們的程序中使用ThreadPool來進行一些比較耗時或者需要阻塞的操作。當學要複雜的同步技術,例如事件,或需要對一個現場表調用Join方法時線程池就不能滿足需求了.在以下情況中不宜使用ThreadPool而應該使用單獨的Thread 

private void button1_Click(object sender, EventArgs e) { //添加當前主線程名稱“main” Thread.CurrentThread.Name = "main"; //使用線程池ThreadPool創建線程 ThreadPool.QueueUserWorkItem(Add); Debug.WriteLine(Thread.CurrentThread.Name); } //這裏加obj參數是爲了適應委託格式 public delegate void WaitCallback(object state) //包含回調方法要使用的信息的對象。 void Add(object obj) { for (int i = 0; i < 100000; i++) { //輸出當前執行操作的現場 Debug.WriteLine(Thread.CurrentThread.Name+i); } }
可以同樣用委託再委託的方法 調用帶有參數(或沒有任何參數)的方法
private void button1_Click(object sender, EventArgs e) { //添加當前主線程名稱“main” Thread.CurrentThread.Name = "main"; //使用線程池ThreadPool創建線程 ThreadPool.QueueUserWorkItem(delegate { Add(333, 44); }, "111"); Debug.WriteLine(Thread.CurrentThread.Name); } void Add(object obj,int q) { for (int i = 0; i < q; i++) { //輸出當前執行操作的現場 Debug.WriteLine(Thread.CurrentThread.Name+i); } }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章