C# 特性(Attribute)
C# 反射(Reflection)
C# 屬性(Property)
C# 索引器(Indexer)
C# 委託(Delegate)
C# 事件(Event)
事件(Event) 基本上說是一個用戶操作,如按鍵、點擊、鼠標移動等等,或者是一些出現,如系統生成的通知。應用程序需要在事件發生時響應事件。例如,中斷。事件是用於進程間通信。
通過事件使用委託
事件在類中聲明且生成,且通過使用同一個類或其他類中的委託與事件處理程序關聯。包含事件的類用於發佈事件。這被稱爲 發佈器(publisher) 類。其他接受該事件的類被稱爲 訂閱器(subscriber) 類。事件使用 發佈-訂閱(publisher-subscriber) 模型。
發佈器(publisher) 是一個包含事件和委託定義的對象。事件和委託之間的聯繫也定義在這個對象中。發佈器(publisher)類的對象調用這個事件,並通知其他的對象。
訂閱器(subscriber) 是一個接受事件並提供事件處理程序的對象。在發佈器(publisher)類中的委託調用訂閱器(subscriber)類中的方法(事件處理程序)。
聲明事件(Event)
在類的內部聲明事件,首先必須聲明該事件的委託類型。例如:
public delegate void BoilerLogHandler(string status);
然後,聲明事件本身,使用 event 關鍵字:
// 基於上面的委託定義事件 public event BoilerLogHandler BoilerEventLog;
上面的代碼定義了一個名爲 BoilerLogHandler 的委託和一個名爲 BoilerEventLog 的事件,該事件在生成的時候會調用委託。
實例 1
using System; namespace SimpleEvent { using System; public class EventTest { private int value; public delegate void NumManipulationHandler(); public event NumManipulationHandler ChangeNum; protected virtual void OnNumChanged() { if (ChangeNum != null) { ChangeNum(); } else { Console.WriteLine("Event fired!"); } } public EventTest(int n ) { SetValue(n); } public void SetValue(int n) { if (value != n) { value = n; OnNumChanged(); } } } public class MainClass { public static void Main() { EventTest e = new EventTest(5); e.SetValue(7); e.SetValue(11); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
Event Fired! Event Fired! Event Fired!
實例 2
本實例提供一個簡單的用於熱水鍋爐系統故障排除的應用程序。當維修工程師檢查鍋爐時,鍋爐的溫度和壓力會隨着維修工程師的備註自動記錄到日誌文件中。
using System; using System.IO; namespace BoilerEventAppl { // boiler 類 class Boiler { private int temp; private int pressure; public Boiler(int t, int p) { temp = t; pressure = p; } public int getTemp() { return temp; } public int getPressure() { return pressure; } } // 事件發佈器 class DelegateBoilerEvent { public delegate void BoilerLogHandler(string status); // 基於上面的委託定義事件 public event BoilerLogHandler BoilerEventLog; public void LogProcess() { string remarks = "O. K"; Boiler b = new Boiler(100, 12); int t = b.getTemp(); int p = b.getPressure(); if(t > 150 || t < 80 || p < 12 || p > 15) { remarks = "Need Maintenance"; } OnBoilerEventLog("Logging Info:\n"); OnBoilerEventLog("Temparature " + t + "\nPressure: " + p); OnBoilerEventLog("\nMessage: " + remarks); } protected void OnBoilerEventLog(string message) { if (BoilerEventLog != null) { BoilerEventLog(message); } } } // 該類保留寫入日誌文件的條款 class BoilerInfoLogger { FileStream fs; StreamWriter sw; public BoilerInfoLogger(string filename) { fs = new FileStream(filename, FileMode.Append, FileAccess.Write); sw = new StreamWriter(fs); } public void Logger(string info) { sw.WriteLine(info); } public void Close() { sw.Close(); fs.Close(); } } // 事件訂閱器 public class RecordBoilerInfo { static void Logger(string info) { Console.WriteLine(info); }//end of Logger static void Main(string[] args) { BoilerInfoLogger filelog = new BoilerInfoLogger("e:\\boiler.txt"); DelegateBoilerEvent boilerEvent = new DelegateBoilerEvent(); boilerEvent.BoilerEventLog += new DelegateBoilerEvent.BoilerLogHandler(Logger); boilerEvent.BoilerEventLog += new DelegateBoilerEvent.BoilerLogHandler(filelog.Logger); boilerEvent.LogProcess(); Console.ReadLine(); filelog.Close(); }//end of main }//end of RecordBoilerInfo }
當上面的代碼被編譯和執行時,它會產生下列結果:
Logging info: Temperature 100 Pressure 12 Message: O. K
C# 集合(Collection)
C# 泛型(Generic)
C# 匿名方法
C# 不安全代碼
C# 多線程
線程 被定義爲程序的執行路徑。每個線程都定義了一個獨特的控制流。如果您的應用程序涉及到複雜的和耗時的操作,那麼設置不同的線程執行路徑往往是有益的,每個線程執行特定的工作。
線程是輕量級進程。一個使用線程的常見實例是現代操作系統中並行編程的實現。使用線程節省了 CPU 週期的浪費,同時提高了應用程序的效率。
到目前爲止我們編寫的程序是一個單線程作爲應用程序的運行實例的單一的過程運行的。但是,這樣子應用程序同時只能執行一個任務。爲了同時執行多個任務,它可以被劃分爲更小的線程。
線程生命週期
線程生命週期開始於 System.Threading.Thread 類的對象被創建時,結束於線程被終止或完成執行時。
下面列出了線程生命週期中的各種狀態:
- 未啓動狀態:當線程實例被創建但 Start 方法未被調用時的狀況。
- 就緒狀態:當線程準備好運行並等待 CPU 週期時的狀況。
-
不可運行狀態:下面的幾種情況下線程是不可運行的:
- 已經調用 Sleep 方法
- 已經調用 Wait 方法
- 通過 I/O 操作阻塞
- 死亡狀態:當線程已完成執行或已中止時的狀況。
主線程
在 C# 中,System.Threading.Thread 類用於線程的工作。它允許創建並訪問多線程應用程序中的單個線程。進程中第一個被執行的線程稱爲主線程。
當 C# 程序開始執行時,主線程自動創建。使用 Thread 類創建的線程被主線程的子線程調用。您可以使用 Thread 類的 CurrentThread屬性訪問線程。
下面的程序演示了主線程的執行:
using System; using System.Threading; namespace MultithreadingApplication { class MainThreadProgram { static void Main(string[] args) { Thread th = Thread.CurrentThread; th.Name = "MainThread"; Console.WriteLine("This is {0}", th.Name); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
This is MainThread
Thread 類常用的屬性和方法
下表列出了 Thread 類的一些常用的 屬性:
屬性 | 描述 |
---|---|
CurrentContext | 獲取線程正在其中執行的當前上下文。 |
CurrentCulture | 獲取或設置當前線程的區域性。 |
CurrentPrinciple | 獲取或設置線程的當前負責人(對基於角色的安全性而言)。 |
CurrentThread | 獲取當前正在運行的線程。 |
CurrentUICulture | 獲取或設置資源管理器使用的當前區域性以便在運行時查找區域性特定的資源。 |
ExecutionContext | 獲取一個 ExecutionContext 對象,該對象包含有關當前線程的各種上下文的信息。 |
IsAlive | 獲取一個值,該值指示當前線程的執行狀態。 |
IsBackground | 獲取或設置一個值,該值指示某個線程是否爲後臺線程。 |
IsThreadPoolThread | 獲取一個值,該值指示線程是否屬於託管線程池。 |
ManagedThreadId | 獲取當前託管線程的唯一標識符。 |
Name | 獲取或設置線程的名稱。 |
Priority | 獲取或設置一個值,該值指示線程的調度優先級。 |
ThreadState | 獲取一個值,該值包含當前線程的狀態。 |
下表列出了 Thread 類的一些常用的 方法:
序號 | 方法名 & 描述 |
---|---|
1 |
public void Abort() 在調用此方法的線程上引發 ThreadAbortException,以開始終止此線程的過程。調用此方法通常會終止線程。 |
2 |
public static LocalDataStoreSlot AllocateDataSlot() 在所有的線程上分配未命名的數據槽。爲了獲得更好的性能,請改用以 ThreadStaticAttribute 屬性標記的字段。 |
3 |
public static LocalDataStoreSlot AllocateNamedDataSlot( string name) 在所有線程上分配已命名的數據槽。爲了獲得更好的性能,請改用以 ThreadStaticAttribute 屬性標記的字段。 |
4 |
public static void BeginCriticalRegion() 通知主機執行將要進入一個代碼區域,在該代碼區域內線程中止或未經處理的異常的影響可能會危害應用程序域中的其他任務。 |
5 |
public static void BeginThreadAffinity() 通知主機託管代碼將要執行依賴於當前物理操作系統線程的標識的指令。 |
6 |
public static void EndCriticalRegion() 通知主機執行將要進入一個代碼區域,在該代碼區域內線程中止或未經處理的異常僅影響當前任務。 |
7 |
public static void EndThreadAffinity() 通知主機託管代碼已執行完依賴於當前物理操作系統線程的標識的指令。 |
8 |
public static void FreeNamedDataSlot(string name) 爲進程中的所有線程消除名稱與槽之間的關聯。爲了獲得更好的性能,請改用以 ThreadStaticAttribute 屬性標記的字段。 |
9 |
public static Object GetData( LocalDataStoreSlot slot ) 在當前線程的當前域中從當前線程上指定的槽中檢索值。爲了獲得更好的性能,請改用以 ThreadStaticAttribute 屬性標記的字段。 |
10 |
public static AppDomain GetDomain() 返回當前線程正在其中運行的當前域。 |
11 |
public static AppDomain GetDomainID() 返回唯一的應用程序域標識符。 |
12 |
public static LocalDataStoreSlot GetNamedDataSlot( string name ) 查找已命名的數據槽。爲了獲得更好的性能,請改用以 ThreadStaticAttribute 屬性標記的字段。 |
13 |
public void Interrupt() 中斷處於 WaitSleepJoin 線程狀態的線程。 |
14 |
public void Join() 在繼續執行標準的 COM 和 SendMessage 消息泵處理期間,阻塞調用線程,直到某個線程終止爲止。此方法有不同的重載形式。 |
15 |
public static void MemoryBarrier() 按如下方式同步內存存取:執行當前線程的處理器在對指令重新排序時,不能採用先執行 MemoryBarrier 調用之後的內存存取,再執行 MemoryBarrier 調用之前的內存存取的方式。 |
16 |
public static void ResetAbort() 取消爲當前線程請求的 Abort。 |
17 |
public static void SetData( LocalDataStoreSlot slot, Object data ) 在當前正在運行的線程上爲此線程的當前域在指定槽中設置數據。爲了獲得更好的性能,請改用以 ThreadStaticAttribute 屬性標記的字段。 |
18 |
public void Start() 開始一個線程。 |
19 |
public static void Sleep( int millisecondsTimeout ) 讓線程暫停一段時間。 |
20 |
public static void SpinWait( int iterations ) 導致線程等待由 iterations 參數定義的時間量。 |
21 |
public static byte VolatileRead( ref byte address ) public static double VolatileRead( ref double address ) public static int VolatileRead( ref int address ) public static Object VolatileRead( ref Object address ) 讀取字段值。無論處理器的數目或處理器緩存的狀態如何,該值都是由計算機的任何處理器寫入的最新值。此方法有不同的重載形式。這裏只給出了一些形式。 |
22 |
public static void VolatileWrite( ref byte address, byte value ) public static void VolatileWrite( ref double address, double value ) public static void VolatileWrite( ref int address, int value ) public static void VolatileWrite( ref Object address, Object value ) 立即向字段寫入一個值,以使該值對計算機中的所有處理器都可見。此方法有不同的重載形式。這裏只給出了一些形式。 |
23 |
public static bool Yield() 導致調用線程執行準備好在當前處理器上運行的另一個線程。由操作系統選擇要執行的線程。 |
創建線程
線程是通過擴展 Thread 類創建的。擴展的 Thread 類調用 Start() 方法來開始子線程的執行。
下面的程序演示了這個概念:
using System; using System.Threading; namespace MultithreadingApplication { class ThreadCreationProgram { public static void CallToChildThread() { Console.WriteLine("Child thread starts"); } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("In Main: Creating the Child thread"); Thread childThread = new Thread(childref); childThread.Start(); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
In Main: Creating the Child thread Child thread starts
管理線程
Thread 類提供了各種管理線程的方法。
下面的實例演示了 sleep() 方法的使用,用於在一個特定的時間暫停線程。
using System; using System.Threading; namespace MultithreadingApplication { class ThreadCreationProgram { public static void CallToChildThread() { Console.WriteLine("Child thread starts"); // 線程暫停 5000 毫秒 int sleepfor = 5000; Console.WriteLine("Child Thread Paused for {0} seconds", sleepfor / 1000); Thread.Sleep(sleepfor); Console.WriteLine("Child thread resumes"); } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("In Main: Creating the Child thread"); Thread childThread = new Thread(childref); childThread.Start(); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
In Main: Creating the Child thread Child thread starts Child Thread Paused for 5 seconds Child thread resumes
銷燬線程
Abort() 方法用於銷燬線程。
通過拋出 threadabortexception 在運行時中止線程。這個異常不能被捕獲,如果有 finally 塊,控制會被送至 finally 塊。
下面的程序說明了這點:
using System; using System.Threading; namespace MultithreadingApplication { class ThreadCreationProgram { public static void CallToChildThread() { try { Console.WriteLine("Child thread starts"); // 計數到 10 for (int counter = 0; counter <= 10; counter++) { Thread.Sleep(500); Console.WriteLine(counter); } Console.WriteLine("Child Thread Completed"); } catch (ThreadAbortException e) { Console.WriteLine("Thread Abort Exception"); } finally { Console.WriteLine("Couldn't catch the Thread Exception"); } } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("In Main: Creating the Child thread"); Thread childThread = new Thread(childref); childThread.Start(); // 停止主線程一段時間 Thread.Sleep(2000); // 現在中止子線程 Console.WriteLine("In Main: Aborting the Child thread"); childThread.Abort(); Console.ReadKey(); } } }
當上面的代碼被編譯和執行時,它會產生下列結果:
In Main: Creating the Child thread Child thread starts 0 1 2 In Main: Aborting the Child thread Thread Abort Exception Couldn't catch the Thread Exception