對於所有需要等待 的操作,例 如 ,因 為文件 、 數據庫或網絡訪 問都需要一定 的時間,此 時就可以啟 動一個新線程,同時完成其他任務,即使是處理密集型的任務,線程也是有幫助的。
Parallel.For()方法類似於C#的For循環,多次執行一個任務,它可以並行運行迭代。迭代的順序沒有定義。
ParallelLoopResult result = Parallel.For(0, 10, i => { Console.WriteLine("{0},task:{1},thread:{2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10); }); Console.WriteLine(result.IsCompleted);
在For()方法中,前兩個參數定義了循環的開頭和結束。從輸出可以看出,順序是不能保證的。也可以提前中斷Parallel.For()方法。
ParallelLoopResult result2 = Parallel.For(10, 40, (int i,ParallelLoopState pls) => { Console.WriteLine("i: {0},task:{1}", i, Task.CurrentId); Thread.Sleep(10); if (i > 15) pls.Break(); }); Console.WriteLine(result2.IsCompleted); Console.WriteLine( "lowest break iteration:{0}",result2.LowestBreakIteration);
paraller.ForEach()方法遍歷實現了IEnumerable的集合,其方式類似於Foreach語句,但以異步方式遍歷,這裡也沒有確定的遍歷順序。
string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve" }; // Parallel.ForEach(data, s => { Console.WriteLine(s); }); Parallel.ForEach(data, (s, pls) => { if (s == "one") { Console.WriteLine("break......"); pls.Break(); } Console.WriteLine(s); Thread.Sleep(100); });
Parallel.Invoke(Foo ,Bar); static void Foo() { Console.WriteLine("foo"); } static void Bar() { Console.WriteLine("bar"); }
.NET 4 包含新的名稱空間System.Threading.Task,它它 包含的類抽象出了線程功能,在後台使用ThreadPool。 任務表示應完成的某個單元的工作。 這個單元的工作可以在單獨的線程中運行,也可以以同步方式啟動一個任務,這需要等待主調線程。
要啟動任務,可 以使用 TaskFactory類 或 Task類 的構造函數和 start()方 法。 Task類 的構造函數在創建任務上提供的靈活性較大.
//using TaskFactory Task t1 = new TaskFactory().StartNew(TaskMethod); //using the task factory via task Task t2 = Task.Factory.StartNew(TaskMethod); //using task constructor Task t3 = new Task(TaskMethod); t3.Start();
使用 Task類 的構造函數和 TaskFactory類 的 stamw()方法時,都可以傳遞TaskCreationOptions枚舉中的值。 設置LongRunning選項,可 以通知任務調度器,該 任務需要較長時間執行,這樣調度器更可能使用 新線。 如果該任務應關聯到父任務上,而父任務取消了,則 該任務也應取消,此 時應設置 AuachToParent選 項。PerferFairness 值表示,調度器應提取出已在等待的第一個任務。 如果任務使用 子任務創建了其他工作,子
任務就優先於其他任務。 它們不會排在線程池隊列中的最後。 如果這些任務應 以公平的方式與所有其他任務一起處理,就設置該選項為PreferFairness
Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness);
通過任務,可 以指定在任務完成後,應 開始運行另一個特定任務.
static void DoOnFirst() { Console.WriteLine("doing some task {0}",Task.CurrentId); Thread.Sleep(3000); } static void DoSecond(Task t) { Console.WriteLine("task {0} finished",t.Id); Console.WriteLine("this task id {0}",Task.CurrentId); Console.WriteLine("do some cleanup"); Thread.Sleep(3000); } Task t1 = new Task(DoOnFirst); Task t2 = t1.ContinueWith(DoSecond); Task t3 = t2.ContinueWith(DoSecond); Task t4 = t3.ContinueWith(DoSecond); Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness); t1.Start();
無論前一個任務是如何結束的,前 面 的連續任務總是在前一個任務結束時啟 動 。 使用TaskContinuationOptions 枚舉中的值,可 以指定,連續任務只有在起始任務成功(或失敗)結束時啟動。
static void ParentAndChild() { var parent = new Task(ParentTask); parent.Start(); Thread.Sleep(2000); Console.WriteLine(parent.Status); Thread.Sleep(4000); Console.WriteLine(parent.Status); Console.WriteLine(); } private static void ParentTask() { Console.WriteLine("task id {0}",Task.CurrentId); var child = new Task(ChildTask); child.Start(); Thread.Sleep(1000); Console.WriteLine("parent started child"); } private static void ChildTask() { Console.WriteLine("child"); Thread.Sleep(5000); Console.WriteLine("child finished"); }
如果父任務在子任務之前結束 ,父 任務的狀態就顯示為WaitingForChildrenToComplete.只要子任務也結束 時,父任務的狀態就變成RanToCompletion。 ·
4 取消架構
var cts = new CancellationTokenSource(); cts.Token.Register(() => Console.WriteLine("token canceled")); new Task(() => { Thread.Sleep(500); cts.Cancel(false); }).Start(); try { ParallelLoopResult result = Parallel.For(0, 100, new ParallelOptions() { CancellationToken = cts.Token, }, x => { Console.WriteLine("loop {0} started", x); int sun = 0; for (int i = 0; i < 100; i++) { Thread.Sleep(2); sun += i; } Console.WriteLine("loop {0} finished",x); }); } catch (Exception ex) { Console.WriteLine(ex.Message); }
同樣的取消模式也可用於任務。
如果有不同的小任務要完成,就可以事先創建許多線程 ,· 在應完成這些任務時發出請求。 這個線程數最好在需要更多的線程時增加,在 需要釋放資源時減少。不需要自己創建這樣一個列表。 該列表由 ThreadPool類 托管。 這個類會在需要時增減池中線程的線程數,直 到最大的線程數。 池中的最大線程數是可配置的。如果有更多的作業要處理,線 程池中線程的個數也到了極限,最 新的作業就要排隊,且 必須等待線程完成其任務。
static void Main(string[] args) { int nWorkerThreads; int nCompletionPortThreads; ThreadPool.GetMaxThreads(out nWorkerThreads, out nCompletionPortThreads); Console.WriteLine("nWorkerThreads:{0},nCompletionPortThreads:{1}", nWorkerThreads, nCompletionPortThreads); for (int i = 0; i < 5; i++) { ThreadPool.QueueUserWorkItem(JobForAThread); } Thread.Sleep(3000); Console.ReadKey(); } static void JobForAThread(object obj) { for (int i = 0; i < 3; i++) { Console.WriteLine("loop:{0},running inside pooled thread{1}",i,Thread.CurrentThread.ManagedThreadId); Thread.Sleep(30); } }View Code
線程池使用起來很簡單,但 它有一些限制 :
使用Thread類可以創建和控制線程,
new Thread(() => { Console.WriteLine("Running in thread"); }).Start(); Console.WriteLine("this is the main thread");
給線程傳遞一些數據可以采用2中方式,一種是使用帶ParameterizdThreadStart委托參數的Thread構造函數,另一種方式是常見一個自定義的類,把線程的方法定義為實例方法。
只要有一個前台相稱在運行,程序的進程就在運行,如果前台多個線程在運行,而Main()方法結束了,應用程序的進程直到所有前台完成其任務前都處於激活狀態。默認情況下,用Thread創建的線程為前台線程,線程池中的線程為總是為後台線程。Thread類可以設置IsBackground屬性設置是否為前台線程。
static void Main(string[] args) { var t1 = new Thread(ThreadMain) { Name = "NewThread", IsBackground = false }; t1.Start(); Console.WriteLine("Main thread ending now"); Console.ReadKey(); } static void ThreadMain() { Console.WriteLine("Thread {0} statrted",Thread.CurrentThread.Name); Thread.Sleep(5000); Console.WriteLine("Thread {0} completed",Thread.CurrentThread.Name); }View Code
線 程曲操作系統調度。 給線程指定優先級,就 可 以影響調度順序。在Thread類中,可以設置Priority屬性設置線程的優先級,Priority屬性需要ThreadPriority枚舉定義的一個值,定義級別有Highest,AboveNormal,Normal,BelowNormal和Lowest。
調用 Thread對 象的Start()方 法,可 以創建線程。 但是,在 調用Strat()方法後,新線程仍不是處於 Running狀態,而 是處於 Unstarted狀 態。 只要操作系統的線程調度器選擇了要運行的線程,線程就會改為Running狀態 。 讀取Thread.ThreadState屬 性,就可以獲得線程的當前狀態。使用 Thread.Sleep() 方法 ,會使線程處於WaitSleepJoin狀態,在 經歷Sleep()方法定義的時間段後 ,線程就會等待再次被喚醒。要停止另一個線程,可 以調用Thread.Abort()方 法。 調用這個方法時,會 在接到終止命令的線程中拋出一個ThreadAbortException類 型的異常。 用一個處理程序捕獲這個異常,線程可 以在結束前完成一些清理工作。如 果需要等待線程的結束,就 可 以調用Thread.Join()方 法 。此方 法會停止當前線程 ,並把它設置為WaitSleepJoin狀 態 ,直 到加入 的線程完成為止 。
如果兩個或多個線程訪問相同的對象,或 者訪問不同步的共享狀態,就會出現爭用條件。
過多的鎖定也會有麻煩。 在死鎖中,至少有兩個線程被掛起,並等待對方解除鎖定。 由於兩個線程都在等待對方,就 出現了死鎖,線程將無限等待下去。
C#為多個線程的同步提供了 自己的關鍵字:lock語 句 。 lock語 句是設置鎖定和解除鎖定的一種簡單方式。
static void Main() { int numTask = 20; var state = new ShareState(); var tasks = new Task[numTask]; for (int i = 0; i < numTask; i++) { tasks[i] = new Task(new Job(state).DoWork); tasks[i].Start(); } for (int i = 0; i < numTask; i++) { tasks[i].Wait(); } Console.WriteLine("Sun :{0}",state.State); Console.ReadKey(); } } public class Job { ShareState shareState; public Job(ShareState shareState) { this.shareState = shareState; } public void DoWork() { for (int i = 0; i < 5000; i++) { shareState.State += 1; } } } public class ShareState { public int State { get; set; } }View Code
上面的代碼,因為執行了5000次循環,有20個任務,所以輸出的值應為100000,但是,事實並非如此。使用Lock修改DoWork方法
public void DoWork() { for (int i = 0; i < 5000; i++) { lock (shareState) shareState.State += 1; } }View Code
這樣結果總是正確的。但是在一個地方使用Lock語句並不意味著,訪問對象的其他線程都在等待,必須對每個訪問共享狀態的線程顯示的使用同步功能。繼續需改
static void Main() { int numTask = 20; var state = new ShareState(); var tasks = new Task[numTask]; for (int i = 0; i < numTask; i++) { tasks[i] = new Task(new Job(state).DoWork); tasks[i].Start(); } for (int i = 0; i < numTask; i++) { tasks[i].Wait(); } Console.WriteLine("Sun :{0}", state.State); Console.ReadKey(); } } public class Job { ShareState shareState; public Job(ShareState shareState) { this.shareState = shareState; } public void DoWork() { for (int i = 0; i < 5000; i++) { shareState.IncrementState(); } } } public class ShareState { private int state = 0; private object obj = new object(); public int State { get { return state; } } public int IncrementState() { lock(obj) return ++state; } }View Code
Ihterlockcd類用 於使變量的簡單語旬原子化。 i++不是線程安全的,它 的操作包括從內存中獲取一個值,給該值遞增 1,再 將它存儲回內存。 這些操作都可能會被線程調度器打斷。 Ihterlocked類提供了以線程安全的方式遞增、 遞減、'交換和讀取值的方法。 與其他同步技術相 比,使用 Ihterlocked類 會快得多。 但是,它 只能用於簡單的同步問題。
C#的lock語 句 ,由編譯器解析為使用monitor類,與C#的 lock語 句相 比,Monitor 類的主要優點是:可 以添加一個等待被鎖定的超時值 。 這樣就不會無限期地等待被鎖定.
object obj = new object(); bool lockTaken = false; Monitor.TryEnter(obj, 500, ref lockTaken); if (lockTaken) { try { //已經鎖定,想干嘛就干嘛吧 } finally { Monitor.Exit(obj); } } else { //沒有鎖定,小心喽 }View Code
Mutex【Mutual exclusion ,互 斥)是.Net Freamwork中 提供跨多個進程同步訪問的一個類 由於系統能識別有名稱的互斥,因 此可 以使用 它禁止應用程序啟動兩次
static class Program { /// <summary> /// 應用程序的主入口點。 /// </summary> [STAThread] static void Main() { bool createNew; Mutex m = new Mutex(false, "test", out createNew); if (!createNew) { MessageBox.Show("程序已啟動"); Application.Exit(); return; } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } }View Code