1. 程式人生 > >C#綜合揭祕——細說多執行緒

C#綜合揭祕——細說多執行緒

轉載自:http://www.cnblogs.com/leslies2/archive/2012/02/07/2320914.html 


引言

  本文主要從執行緒的基礎用法,CLR執行緒池當中工作者執行緒與I/O執行緒的開發,並行操作PLINQ等多個方面介紹多執行緒的開發。

  其中委託的BeginInvoke方法以及回撥函式最為常用。

  而 I/O執行緒可能容易遭到大家的忽略,其實在開發多執行緒系統,更應該多留意I/O執行緒的操作。特別是在ASP.NET開發當中,可能更多人只會留意在客戶端使用Ajax或者在伺服器端使用UpdatePanel。其實合理使用I/O執行緒在通訊專案或檔案下載時,能儘可能地減少IIS的壓力。

  並行程式設計是Framework4.0中極力推廣的非同步操作方式,更值得更深入地學習。

  希望本篇文章能對各位的學習研究有所幫助,當中有所錯漏的地方敬請點評。

  目錄

  一、執行緒的定義

  二、執行緒的基礎知識

  三、以ThreadStart方式實現多執行緒

  四、CLR執行緒池的工作者執行緒

  一、執行緒的定義

   1. 1 程序、應用程式域與執行緒的關係

  程序(Process)是Windows系統中的一個基本概念,它包含著一個執行程式所需要的資源。程序之間是相對獨立的,一個程序無法訪問另一個程序的資料(除非利用分散式計算方式),一個程序執行的失敗也不會影響其他程序的執行,Windows系統就是利用程序把工作劃分為多個獨立的區域的。程序可以理解為一個程式的基本邊界。

  應用程式域(AppDomain)是一個程式執行的邏輯區域,它可以視為一個輕量級的程序,.NET的程式集正是在應用程式域中執行的,一個程序可以包含有多個應用程式域,一個應用程式域也可以包含多個程式集。在一個應用程式域中包含了一個或多個上下文context,使用上下文CLR就能夠把某些特殊物件的狀態放置在不同容器當中。

  執行緒(Thread)是程序中的基本執行單元,在程序入口執行的第一個執行緒被視為這個程序的主執行緒。在.NET應用程式中,都是以Main()方法作為入口的,當呼叫此方法時系統就會自動建立一個主執行緒。執行緒主要是由CPU暫存器、呼叫棧和執行緒本地儲存器(Thread Local Storage,TLS)組成的。CPU暫存器主要記錄當前所執行執行緒的狀態,呼叫棧主要用於維護執行緒所呼叫到的記憶體與資料,TLS主要用於存放執行緒的狀態資訊。

  程序、應用程式域、執行緒的關係如下圖,一個程序內可以包括多個應用程式域,也有包括多個執行緒,執行緒也可以穿梭於多個應用程式域當中。但在同一個時刻,執行緒只會處於一個應用程式域內。 

  由於本文是以介紹多執行緒技術為主題,對程序、應用程式域的介紹就到此為止。關於程序、執行緒、應用程式域的技術,在“C#綜合揭祕——細說程序執行緒與應用程式域”會有詳細介紹。

  1. 2 多執行緒

  在單CPU系統的一個單位時間(time slice)內,CPU只能執行單個執行緒,執行順序取決於執行緒的優先級別。如果在單位時間內執行緒未能完成執行,系統就會把執行緒的狀態資訊儲存到執行緒的本地儲存器(TLS) 中,以便下次執行時恢復執行。而多執行緒只是系統帶來的一個假像,它在多個單位時間內進行多個執行緒的切換。因為切換頻密而且單位時間非常短暫,所以多執行緒可被視作同時執行。

  適當使用多執行緒能提高系統的效能,比如:在系統請求大容量的資料時使用多執行緒,把資料輸出工作交給非同步執行緒,使主執行緒保持其穩定性去處理其他問題。但需要注意一點,因為CPU需要花費不少的時間線上程的切換上,所以過多地使用多執行緒反而會導致效能的下降。

  二、執行緒的基礎知識

  2. 1 System.Threading.Thread類

  System.Threading.Thread是用於控制執行緒的基礎類,通過Thread可以控制當前應用程式域中執行緒的建立、掛起、停止、銷燬。

  它包括以下常用公共屬性:

屬性名稱 說明
CurrentContext 獲取執行緒正在其中執行的當前上下文。
CurrentThread 獲取當前正在執行的執行緒。
ExecutionContext 獲取一個 ExecutionContext 物件,該物件包含有關當前執行緒的各種上下文的資訊。
IsAlive 獲取一個值,該值指示當前執行緒的執行狀態。
IsBackground 獲取或設定一個值,該值指示某個執行緒是否為後臺執行緒。
IsThreadPoolThread 獲取一個值,該值指示執行緒是否屬於託管執行緒池。
ManagedThreadId 獲取當前託管執行緒的唯一識別符號。
Name 獲取或設定執行緒的名稱。
Priority 獲取或設定一個值,該值指示執行緒的排程優先順序。
ThreadState 獲取一個值,該值包含當前執行緒的狀態。

  2. 1.1 執行緒的識別符號

  ManagedThreadId是確認執行緒的唯一識別符號,程式在大部分情況下都是通過Thread.ManagedThreadId來辨別執行緒的。而Name是一個可變值,在預設時候,Name為一個空值 Null,開發人員可以通過程式設定執行緒的名稱,但這只是一個輔助功能。  

  2. 1.2 執行緒的優先級別

  .NET為執行緒設定了Priority屬性來定義執行緒執行的優先級別,裡面包含5個選項,其中Normal是預設值。除非系統有特殊要求,否則不應該隨便設定執行緒的優先級別。  

成員名稱 說明
Lowest 可以將 Thread 安排在具有任何其他優先順序的執行緒之後。
BelowNormal 可以將 Thread 安排在具有 Normal 優先順序的執行緒之後,在具有 Lowest 優先順序的執行緒之前。
Normal 預設選擇。可以將 Thread 安排在具有 AboveNormal 優先順序的執行緒之後,在具有 BelowNormal 優先順序的執行緒之前。
AboveNormal 可以將 Thread 安排在具有 Highest 優先順序的執行緒之後,在具有 Normal 優先順序的執行緒之前。
Highest 可以將 Thread 安排在具有任何其他優先順序的執行緒之前。

  2. 1.3 執行緒的狀態

  通過ThreadState可以檢測執行緒是處於Unstarted、Sleeping、Running 等等狀態,它比 IsAlive 屬效能提供更多的特定資訊。

  前面說過,一個應用程式域中可能包括多個上下文,而通過CurrentContext可以獲取執行緒當前的上下文。

  CurrentThread是最常用的一個屬性,它是用於獲取當前執行的執行緒。  

  2. 1.4 System.Threading.Thread的方法

  Thread 中包括了多個方法來控制執行緒的建立、掛起、停止、銷燬,以後來的例子中會經常使用。

方法名稱 說明
Abort() 終止本執行緒。
GetDomain() 返回當前執行緒正在其中執行的當前域。
GetDomainId() 返回當前執行緒正在其中執行的當前域Id。
Interrrupt() 中斷處於 WaitSleepJoin 執行緒狀態的執行緒。
Join() 已過載。 阻塞呼叫執行緒,直到某個執行緒終止時為止。
Resume() 繼續執行已掛起的執行緒。
Start() 執行本執行緒。
Suspend() 掛起當前執行緒,如果當前執行緒已屬於掛起狀態則此不起作用
Sleep() 把正在執行的執行緒掛起一段時間。

  2. 1.5 開發例項

  以下這個例子,就是通過Thread顯示當前執行緒資訊

static void Main(string[] args)
{
Thread thread = Thread.CurrentThread;
thread.Name = "Main Thread";
string threadMessage = string.Format("Thread ID:{0}\n Current AppDomainId:{1}\n "+
"Current ContextId:{2}\n Thread Name:{3}\n "+
"Thread State:{4}\n Thread Priority:{5}\n",
thread.ManagedThreadId, Thread.GetDomainID(), Thread.CurrentContext.ContextID,
thread.Name, thread.ThreadState, thread.Priority);
Console.WriteLine(threadMessage);
Console.ReadKey();
}

  執行結果

  2. 2  System.Threading 名稱空間

  在System.Threading名稱空間內提供多個方法來構建多執行緒應用程式,其中ThreadPool與Thread是多執行緒開發中最常用到的,在.NET中專門設定了一個CLR執行緒池專門用於管理執行緒的執行,這個CLR執行緒池正是通過ThreadPool類來管理。而Thread是管理執行緒的最直接方式,下面幾節將詳細介紹有關內容。

類     說明
AutoResetEvent 通知正在等待的執行緒已發生事件。無法繼承此類。
ExecutionContext 管理當前執行緒的執行上下文。無法繼承此類。
Interlocked 為多個執行緒共享的變數提供原子操作。
Monitor 提供同步對物件的訪問的機制。
Mutex 一個同步基元,也可用於程序間同步。
Thread 建立並控制執行緒,設定其優先順序並獲取其狀態。
ThreadAbortException 在對 Abort 方法進行呼叫時引發的異常。無法繼承此類。
ThreadPool 提供一個執行緒池,該執行緒池可用於傳送工作項、處理非同步 I/O、代表其他執行緒等待以及處理計時器。
Timeout 包含用於指定無限長的時間的常數。無法繼承此類。
Timer 提供以指定的時間間隔執行方法的機制。無法繼承此類。
WaitHandle 封裝等待對共享資源的獨佔訪問的作業系統特定的物件。

  在System.Threading中的包含了下表中的多個常用委託,其中ThreadStart、ParameterizedThreadStart是最常用到的委託。

  由ThreadStart生成的執行緒是最直接的方式,但由ThreadStart所生成並不受執行緒池管理。

  而ParameterizedThreadStart是為非同步觸發帶引數的方法而設的,在下一節將為大家逐一細說。

委託說明
ContextCallback 表示要在新上下文中呼叫的方法。
ParameterizedThreadStart 表示在 Thread 上執行的方法。
ThreadExceptionEventHandler 表示將要處理 Application 的 ThreadException 事件的方法。
ThreadStart 表示在 Thread 上執行的方法。
TimerCallback 表示處理來自 Timer 的呼叫的方法。
WaitCallback 表示執行緒池執行緒要執行的回撥方法。
WaitOrTimerCallback 表示當 WaitHandle 超時或終止時要呼叫的方法。

  2. 3 執行緒的管理方式

  通過ThreadStart來建立一個新執行緒是最直接的方法,但這樣創建出來的執行緒比較難管理,如果建立過多的執行緒反而會讓系統的效能下載。有見及此,.NET為執行緒管理專門設定了一個CLR執行緒池,使用CLR執行緒池系統可以更合理地管理執行緒的使用。所有請求的服務都能運行於執行緒池中,當執行結束時執行緒便會迴歸到執行緒池。通過設定,能控制執行緒池的最大執行緒數量,在請求超出執行緒最大值時,執行緒池能按照操作的優先級別來執行,讓部分操作處於等待狀態,待有執行緒迴歸時再執行操作。

  基礎知識就為大家介紹到這裡,下面將詳細介紹多執行緒的開發。

  三、以ThreadStart方式實現多執行緒

  3. 1 使用ThreadStart委託

  這裡先以一個例子體現一下多執行緒帶來的好處,首先在Message類中建立一個方法ShowMessage(),裡面顯示了當前執行執行緒的Id,並使用Thread.Sleep(int ) 方法模擬部分工作。在main()中通過ThreadStart委託繫結Message物件的ShowMessage()方法,然後通過Thread.Start()執行非同步方法。

public class Message
{
public void ShowMessage()
{
string message = string.Format("Async threadId is :{0}",
Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(message);

for (int n = 0; n < 10; n++)
{
Thread.Sleep(300);
Console.WriteLine("The number is:" + n.ToString());
}
}
}

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main threadId is:"+
Thread.CurrentThread.ManagedThreadId);
Message message=new Message();
Thread thread = new Thread(new ThreadStart(message.ShowMessage));
thread.Start();
Console.WriteLine("Do something ..........!");
Console.WriteLine("Main thread working is complete!");

}
}

  請注意執行結果,在呼叫Thread.Start()方法後,系統以非同步方式執行Message.ShowMessage(),而主執行緒的操作是繼續執行的,在Message.ShowMessage()完成前,主執行緒已完成所有的操作。

  3. 2 使用ParameterizedThreadStart委託

  ParameterizedThreadStart委託與ThreadStart委託非常相似,但ParameterizedThreadStart委託是面向帶引數方法的。注意ParameterizedThreadStart 對應方法的引數為object,此引數可以為一個值物件,也可以為一個自定義物件。

public class Person
{
public string Name
{
get;
set;
}
public int Age
{
get;
set;
}
}

public class Message
{
public void ShowMessage(object person)
{
if (person != null)
{
Person _person = (Person)person;
string message = string.Format("\n{0}'s age is {1}!\nAsync threadId is:{2}",
_person.Name,_person.Age,Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(message);
}
for (int n = 0; n < 10; n++)
{
Thread.Sleep(300);
Console.WriteLine("The number is:" + n.ToString());
}
}
}

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main threadId is:"+Thread.CurrentThread.ManagedThreadId);

Message message=new Message();
//繫結帶引數的非同步方法
Thread thread = new Thread(new ParameterizedThreadStart(message.ShowMessage));
Person person = new Person();
person.Name = "Jack";
person.Age = 21;
thread.Start(person); //啟動非同步執行緒

Console.WriteLine("Do something ..........!");
Console.WriteLine("Main thread working is complete!");

}
}

  執行結果:

  3. 3 前臺執行緒與後臺執行緒

  注意以上兩個例子都沒有使用Console.ReadKey(),但系統依然會等待非同步執行緒完成後才會結束。這是因為使用Thread.Start()啟動的執行緒預設為前臺執行緒,而系統必須等待所有前臺執行緒執行結束後,應用程式域才會自動解除安裝。

  在第二節曾經介紹過執行緒Thread有一個屬性IsBackground,通過把此屬性設定為true,就可以把執行緒設定為後臺執行緒!這時應用程式域將在主執行緒完成時就被解除安裝,而不會等待非同步執行緒的執行。

  3. 4 掛起執行緒

  為了等待其他後臺執行緒完成後再結束主執行緒,就可以使用Thread.Sleep()方法。

public class Message
{
public void ShowMessage()
{
string message = string.Format("\nAsync threadId is:{0}",
Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(message);
for (int n = 0; n < 10; n++)
{
Thread.Sleep(300);
Console.WriteLine("The number is:" + n.ToString());
}
}
}

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main threadId is:"+
Thread.CurrentThread.ManagedThreadId);

Message message=new Message();
Thread thread = new Thread(new ThreadStart(message.ShowMessage));
thread.IsBackground = true;
thread.Start();

Console.WriteLine("Do something ..........!");
Console.WriteLine("Main thread working is complete!");
Console.WriteLine("Main thread sleep!");
Thread.Sleep(5000);
}
}

  執行結果如下,此時應用程式域將在主執行緒執行5秒後自動結束

  但系統無法預知非同步執行緒需要執行的時間,所以用通過Thread.Sleep(int)阻塞主執行緒並不是一個好的解決方法。有見及此,.NET專門為等待非同步執行緒完成開發了另一個方法thread.Join()。把上面例子中的最後一行Thread.Sleep(5000)修改為 thread.Join() 就能保證主執行緒在非同步執行緒thread執行結束後才會終止。

  3. 5 Suspend 與 Resume (慎用)

  Thread.Suspend()與 Thread.Resume()是在Framework1.0 就已經存在的老方法了,它們分別可以掛起、恢復執行緒。但在Framework2.0中就已經明確排斥這兩個方法。這是因為一旦某個執行緒佔用了已有的資源,再使用Suspend()使執行緒長期處於掛起狀態,當在其他執行緒呼叫這些資源的時候就會引起死鎖!所以在沒有必要的情況下應該避免使用這兩個方法。

  3. 6 終止執行緒

  若想終止正在執行的執行緒,可以使用Abort()方法。在使用Abort()的時候,將引發一個特殊異常 ThreadAbortException 。

  若想線上程終止前恢復執行緒的執行,可以在捕獲異常後 ,在catch(ThreadAbortException ex){...} 中呼叫Thread.ResetAbort()取消終止。

  而使用Thread.Join()可以保證應用程式域等待非同步執行緒結束後才終止執行。

static void Main(string[] args)
{
Console.WriteLine("Main threadId is:" +
Thread.CurrentThread.ManagedThreadId);

Thread thread = new Thread(new ThreadStart(AsyncThread));
thread.IsBackground = true;
thread.Start();
thread.Join();

}

//以非同步方式呼叫
static void AsyncThread()
{
try

相關推薦

C#綜合揭祕——細說執行

轉載自:http://www.cnblogs.com/leslies2/archive/2012/02/07/2320914.html  引言   本文主要從執行緒的基礎用法,CLR執行緒池當中工作者執行緒與I/O執行緒的開發,並行操作PLINQ等多個方面介紹多執行緒的開發。

c#中委託與執行的實質

delegate(委託)的概念,.Net的委託本質上就是指向函式的指標,只不過這種指標是經過封裝後型別安全的。委託和執行緒是兩個不同的概念,執行緒是動態的,委託就是一個或一組記憶體地址,是靜態的。執行緒執行時如果遇到了指向函式的指標就執行這個函式。.Net為了方便程式設計,給委託賦予了兩種方式以供呼

C#亂彈1—執行

1. 單執行緒        實現,按下button1,控制檯顯示1 2 3 4 ... 100  using System; using System.Collections.Generic; using System.Com

Java併發(十八):阻塞佇列BlockingQueue BlockingQueue(阻塞佇列)詳解 二叉堆(一)之 圖文解析 和 C語言的實現 多執行緒程式設計:阻塞、併發佇列的使用總結 Java併發程式設計:阻塞佇列 java阻塞佇列 BlockingQueue(阻塞佇列)詳解

阻塞佇列(BlockingQueue)是一個支援兩個附加操作的佇列。 這兩個附加的操作是:在佇列為空時,獲取元素的執行緒會等待佇列變為非空。當佇列滿時,儲存元素的執行緒會等待佇列可用。 阻塞佇列常用於生產者和消費者的場景,生產者是往佇列裡新增元素的執行緒,消費者是從佇列裡拿元素的執行緒。阻塞佇列就是生產者

C/S模式下---執行程式設計

伺服器採用單程序/執行緒程式設計,在同一時刻,伺服器只能與一個客戶端進行互動。只有與當前客戶端的通訊結束後,才能為下一個客戶端進行服務。所以,如果採用執行緒,讓主執行緒連線客戶端,而函式執行緒為每個客戶端進行服務,這樣就可以保證伺服器可以同時為多個客戶端提供服務,實現併發。 採用多執

C++11中的執行

C++標準庫的多執行緒使用, 示例程式碼如下: #include<iostream> #include<thread> //C++11 //#include<exc

C++11 併發與執行篇(未完成)

從C++11新標準開始,C++語言本身增加了對多執行緒的支援,意味著使用C++可實現多執行緒程式的可移植,跨平臺。 在標準的C++程式中,主執行緒從main()開始執行,我們自己在C++中建立的執行緒,也需要從一個函式開始執行(這個函式叫做初始函式),一旦這個函式執行完

C#非同步程式設計與執行程式設計

C#5.0推出了非同步程式設計,通過關鍵字async 和 await及返回型別為Task(無返回值的非同步方法)和Task(返回值為T的非同步方法)可以將方法封裝為非同步方法。呼叫非同步方法時,遇到await關鍵字程式會立即返回到呼叫者,直到await後的方法執行完成。包括兩種非同步方式:I

C#基礎系列:執行的常見用法詳解

前言:此篇就主要從博主使用過的幾種多執行緒的用法從應用層面大概介紹下。文中觀點都是博主個人的理解,如果有不對的地方望大家指正~~ 1、多執行緒:使用多個處理控制代碼同時對多個任務進行控制處理的一種技術。據博主的理解,多執行緒就是該應用的主執行緒任命其他多個執行緒去協

綜合應用題:執行複製檔案(知識點:執行、隨機讀寫流)

要求:使用多執行緒複製一個檔案(使用多執行緒複製一個檔案可以加快檔案的複製速度) 程式碼: package 多執行緒複製檔案; import java.io.File; import java.io.FileNotFoundException; impor

C# 使用委託實現執行呼叫窗體的四種方式

1、方法一:使用執行緒      功能描述:在用c#做WinFrom開發的過程中。我們經常需要用到進度條(ProgressBar)用於顯示進度資訊。這時候我們可能就需要用到多執行緒,如果不採用多執行緒控制進度條,視窗很容易假死(無法適時看到進度資訊)。下面

C++任務佇列與執行

摘要:       很多場合之所以使用C++,一方面是由於C++編譯後的native code的高效效能,另一方面是由於C++優秀的併發能力。並行方式有多程序 和多執行緒之分,本章暫且只討論多執行緒,多程序方面的知識會在其他章節具體討論。多執行緒是開發C++伺服器程式非常重要的基礎,如何根據需求具體的設計、

C++使用thread類執行程式設計

C++11中引入了一個用於多執行緒操作的thread類,簡單多執行緒示例: #include <iostream> #include <thread> #include &l

[C++11 std::thread] 使用C++11 編寫 Linux 執行程式

前言 在這個多核時代,如何充分利用每個 CPU 核心是一個繞不開的話題,從需要為成千上萬的使用者同時提供服務的服務端應用程式,到需要同時開啟十幾個頁面,每個頁面都有幾十上百個連結的 web 瀏覽器應用程式,從保持著幾 t 甚或幾 p 的資料的資料庫系統,到手機上的一個有良好使用者響應能力的 app,為了

c++CreateEvent函式在執行中使用及例項

CreateEvent函式詳解參見本部落格文章: HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,BOOL bManualReset, BOOL bInitialState,LPCSTR lpName);bManualReset:TRU

Flex和Bison的C++可重進入—執行解決方案

這樣user可以在解析程式中使用driver or parm這兩個變數.這兩個變數都是從外面通過引數的形式傳給解析程式,那此時bison如何跟flex通訊呢?這時候就不會透過全域性變數yylval等進行通訊了,加入%pure_parser新生成的程式碼中已經沒有yylval這個全域性變量了,在解析器的內部實際

C# Winform專案中執行環境下, 如何跨執行對Window窗體控制元件進行安全訪問?

請嘗試執行這段程式碼, 結果你會發現微軟開發工具會提示, Tb_Text.Text = int_Index.ToString(); 涉及"對Windows窗體控制元件進行執行緒安全呼叫", 並給瞭如下的解決方案:https://msdn.microsoft.com/zh-cn/library/ms171728

【嵌入式Linux C程式設計】Linux執行程式設計

程序——資源分配的最小單位,執行緒——程式執行的最小單位。執行緒是程序的一個執行流,是CPU排程和分派的基本單位,它是比程序更小的能獨立執行的基本單位。一個程序由幾個執行緒組成(擁有很多相對獨立的執行流的使用者程式共享應用程式的大部分資料結構),執行緒與同屬一個程序的其他的執

[C#學習筆記之執行2]執行同步與併發訪問共享資源工具—Lock、Monitor、Mutex、Semaphore

“執行緒同步”的含義         當一個程序啟動了多個執行緒時,如果需要控制這些執行緒的推進順序(比如A執行緒必須等待B和C執行緒執行完畢之後才能繼續執行),則稱這些執行緒需要進行“執行緒同步(thread synchronization)”。         執行緒

c#中的webbrowser 執行 【轉載】

我們在做採集軟體的時候 有些網站通過直接分析html文字是很麻煩的事情 在利用WinForm程式設計的情況下 有一種更好的方式當然是分析HtmlDocument 然而,這HtmlDoucment並不能直接建立 它必須由 WebBroswer控制元件Navigate生成一個頁