委託調用、子線程程調用、與線程池調用
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);
}
}