對於全部須要等待 的操做,例 如 ,因 爲文件 、 數據庫或網絡訪 問都須要必定 的時間,此 時就能夠啓 動一個新線程,同時完成其餘任務,即便是處理密集型的任務,線程也是有幫助的。數據庫
Parallel.For()方法相似於C#的For循環,屢次執行一個任務,它能夠並行運行迭代。迭代的順序沒有定義。安全
1 ParallelLoopResult result = Parallel.For(0, 10, i => 2 { 3 Console.WriteLine("{0},task:{1},thread:{2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId); 4 Thread.Sleep(10); 5 }); 6 Console.WriteLine(result.IsCompleted);
在For()方法中,前兩個參數定義了循環的開頭和結束。從輸出能夠看出,順序是不能保證的。也能夠提早中斷Parallel.For()方法。網絡
1 ParallelLoopResult result2 = Parallel.For(10, 40, (int i,ParallelLoopState pls) => 2 { 3 Console.WriteLine("i: {0},task:{1}", i, Task.CurrentId); 4 Thread.Sleep(10); 5 if (i > 15) 6 pls.Break(); 7 }); 8 Console.WriteLine(result2.IsCompleted); 9 Console.WriteLine( "lowest break iteration:{0}",result2.LowestBreakIteration);
paraller.ForEach()方法遍歷實現了IEnumerable的集合,其方式相似於Foreach語句,但以異步方式遍歷,這裏也沒有肯定的遍歷順序。多線程
1 string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve" }; 2 // Parallel.ForEach(data, s => { Console.WriteLine(s); }); 3 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類 的構造函數在建立任務上提供的靈活性較大.異步
1 //using TaskFactory 2 Task t1 = new TaskFactory().StartNew(TaskMethod); 3 //using the task factory via task 4 Task t2 = Task.Factory.StartNew(TaskMethod); 5 //using task constructor 6 Task t3 = new Task(TaskMethod); 7 t3.Start();
使用 Task類 的構造函數和 TaskFactory類 的 stamw()方法時,均可以傳遞TaskCreationOptions枚舉中的值。 設置LongRunning選項,可 以通知任務調度器,該 任務須要較長時間執行,這樣調度器更可能使用 新線。 若是該任務應關聯到父任務上,而父任務取消了,則 該任務也應取消,此 時應設置 AuachToParent選 項。PerferFairness 值表示,調度器應提取出已在等待的第一個任務。 若是任務使用 子任務建立了其餘工做,子
任務就優先於其餘任務。 它們不會排在線程池隊列中的最後。 若是這些任務應 以公平的方式與全部其餘任務一塊兒處理,就設置該選項爲PreferFairnesside
1 Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness);
經過任務,可 以指定在任務完成後,應 開始運行另外一個特定任務.函數
1 static void DoOnFirst() 2 { 3 Console.WriteLine("doing some task {0}",Task.CurrentId); 4 Thread.Sleep(3000); 5 } 6 static void DoSecond(Task t) 7 { 8 Console.WriteLine("task {0} finished",t.Id); 9 Console.WriteLine("this task id {0}",Task.CurrentId); 10 Console.WriteLine("do some cleanup"); 11 Thread.Sleep(3000); 12 } 13 14 Task t1 = new Task(DoOnFirst); 15 Task t2 = t1.ContinueWith(DoSecond); 16 Task t3 = t2.ContinueWith(DoSecond); 17 Task t4 = t3.ContinueWith(DoSecond); 18 Task t5 = t4.ContinueWith(DoSecond,TaskContinuationOptions.PreferFairness); 19 t1.Start();
不管前一個任務是如何結束的,前 面 的連續任務老是在前一個任務結束時啓 動 。 使用TaskContinuationOptions 枚舉中的值,可 以指定,連續任務只有在起始任務成功(或失敗)結束時啓動。oop
1 static void ParentAndChild() 2 { 3 var parent = new Task(ParentTask); 4 parent.Start(); 5 Thread.Sleep(2000); 6 Console.WriteLine(parent.Status); 7 Thread.Sleep(4000); 8 Console.WriteLine(parent.Status); 9 Console.WriteLine(); 10 } 11 private static void ParentTask() 12 { 13 Console.WriteLine("task id {0}",Task.CurrentId); 14 var child = new Task(ChildTask); 15 child.Start(); 16 Thread.Sleep(1000); 17 Console.WriteLine("parent started child"); 18 } 19 20 private static void ChildTask() 21 { 22 Console.WriteLine("child"); 23 Thread.Sleep(5000); 24 Console.WriteLine("child finished"); 25 }
若是父任務在子任務以前結束 ,父 任務的狀態就顯示爲WaitingForChildrenToComplete.只要子任務也結束 時,父任務的狀態就變成RanToCompletion。 ·this
4 取消架構
1 var cts = new CancellationTokenSource(); 2 cts.Token.Register(() => Console.WriteLine("token canceled")); 3 new Task(() => { Thread.Sleep(500); cts.Cancel(false); }).Start(); 4 try 5 { 6 ParallelLoopResult result = Parallel.For(0, 100, new ParallelOptions() { CancellationToken = cts.Token, }, x => 7 { 8 Console.WriteLine("loop {0} started", x); 9 int sun = 0; 10 for (int i = 0; i < 100; i++) 11 { 12 Thread.Sleep(2); 13 sun += i; 14 } 15 Console.WriteLine("loop {0} finished",x); 16 }); 17 } 18 catch (Exception ex) 19 { 20 Console.WriteLine(ex.Message); 21 }
一樣的取消模式也可用於任務。
若是有不一樣的小任務要完成,就能夠事先建立許多線程 ,· 在應完成這些任務時發出請求。 這個線程數最好在須要更多的線程時增長,在 須要釋放資源時減小。不須要本身建立這樣一個列表。 該列表由 ThreadPool類 託管。 這個類會在須要時增減池中線程的線程數,直 到最大的線程數。 池中的最大線程數是可配置的。若是有更多的做業要處理,線 程池中線程的個數也到了極限,最 新的做業就要排隊,且 必須等待線程完成其任務。
1 static void Main(string[] args) 2 { 3 int nWorkerThreads; 4 int nCompletionPortThreads; 5 ThreadPool.GetMaxThreads(out nWorkerThreads, out nCompletionPortThreads); 6 Console.WriteLine("nWorkerThreads:{0},nCompletionPortThreads:{1}", nWorkerThreads, nCompletionPortThreads); 7 8 for (int i = 0; i < 5; i++) 9 { 10 ThreadPool.QueueUserWorkItem(JobForAThread); 11 } 12 Thread.Sleep(3000); 13 Console.ReadKey(); 14 } 15 static void JobForAThread(object obj) 16 { 17 for (int i = 0; i < 3; i++) 18 { 19 Console.WriteLine("loop:{0},running inside pooled thread{1}",i,Thread.CurrentThread.ManagedThreadId); 20 Thread.Sleep(30); 21 } 22 }
線程池使用起來很簡單,但 它有一些限制 :
使用Thread類能夠建立和控制線程,
1 new Thread(() => { Console.WriteLine("Running in thread"); }).Start(); 2 Console.WriteLine("this is the main thread");
給線程傳遞一些數據能夠採用2中方式,一種是使用帶ParameterizdThreadStart委託參數的Thread構造函數,另外一種方式是常見一個自定義的類,把線程的方法定義爲實例方法。
只要有一個前臺相稱在運行,程序的進程就在運行,若是前臺多個線程在運行,而Main()方法結束了,應用程序的進程直到全部前臺完成其任務前都處於激活狀態。默認狀況下,用Thread建立的線程爲前臺線程,線程池中的線程爲老是爲後臺線程。Thread類能夠設置IsBackground屬性設置是否爲前臺線程。
1 static void Main(string[] args) 2 { 3 var t1 = new Thread(ThreadMain) { Name = "NewThread", IsBackground = false }; 4 t1.Start(); 5 Console.WriteLine("Main thread ending now"); 6 Console.ReadKey(); 7 } 8 9 10 static void ThreadMain() 11 { 12 Console.WriteLine("Thread {0} statrted",Thread.CurrentThread.Name); 13 Thread.Sleep(5000); 14 Console.WriteLine("Thread {0} completed",Thread.CurrentThread.Name); 15 }
線 程曲操做系統調度。 給線程指定優先級,就 可 以影響調度順序。在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語 句是設置鎖定和解除鎖定的一種簡單方式。
1 static void Main() 2 { 3 int numTask = 20; 4 var state = new ShareState(); 5 var tasks = new Task[numTask]; 6 for (int i = 0; i < numTask; i++) 7 { 8 tasks[i] = new Task(new Job(state).DoWork); 9 tasks[i].Start(); 10 } 11 12 for (int i = 0; i < numTask; i++) 13 { 14 tasks[i].Wait(); 15 } 16 Console.WriteLine("Sun :{0}",state.State); 17 Console.ReadKey(); 18 } 19 } 20 public class Job 21 { 22 ShareState shareState; 23 public Job(ShareState shareState) 24 { 25 this.shareState = shareState; 26 } 27 public void DoWork() 28 { 29 for (int i = 0; i < 5000; i++) 30 { 31 shareState.State += 1; 32 } 33 } 34 } 35 public class ShareState 36 { 37 public int State { get; set; } 38 }
上面的代碼,由於執行了5000次循環,有20個任務,因此輸出的值應爲100000,可是,事實並不是如此。使用Lock修改DoWork方法
1 public void DoWork() 2 { 3 for (int i = 0; i < 5000; i++) 4 { 5 lock (shareState) 6 shareState.State += 1; 7 } 8 }
這樣結果老是正確的。可是在一個地方使用Lock語句並不意味着,訪問對象的其餘線程都在等待,必須對每一個訪問共享狀態的線程顯示的使用同步功能。繼續需改
1 static void Main() 2 { 3 int numTask = 20; 4 var state = new ShareState(); 5 var tasks = new Task[numTask]; 6 for (int i = 0; i < numTask; i++) 7 { 8 tasks[i] = new Task(new Job(state).DoWork); 9 tasks[i].Start(); 10 } 11 12 for (int i = 0; i < numTask; i++) 13 { 14 tasks[i].Wait(); 15 } 16 Console.WriteLine("Sun :{0}", state.State); 17 Console.ReadKey(); 18 } 19 } 20 public class Job 21 { 22 ShareState shareState; 23 public Job(ShareState shareState) 24 { 25 this.shareState = shareState; 26 } 27 public void DoWork() 28 { 29 for (int i = 0; i < 5000; i++) 30 { 31 shareState.IncrementState(); 32 } 33 } 34 } 35 public class ShareState 36 { 37 private int state = 0; 38 private object obj = new object(); 39 public int State 40 { 41 get 42 { 43 return state; 44 } 45 } 46 public int IncrementState() 47 { 48 lock(obj) 49 return ++state; 50 } 51 }
Ihterlockcd類用 於使變量的簡單語旬原子化。 i++不是線程安全的,它 的操做包括從內存中獲取一個值,給該值遞增 1,再 將它存儲回內存。 這些操做均可能會被線程調度器打斷。 Ihterlocked類提供了以線程安全的方式遞增、 遞減、'交換和讀取值的方法。 與其餘同步技術相 比,使用 Ihterlocked類 會快得多。 可是,它 只能用於簡單的同步問題。
C#的lock語 句 ,由編譯器解析爲使用monitor類,與C#的 lock語 句相 比,Monitor 類的主要優勢是:可 以添加一個等待被鎖定的超時值 。 這樣就不會無限期地等待被鎖定.
1 object obj = new object(); 2 bool lockTaken = false; 3 Monitor.TryEnter(obj, 500, ref lockTaken); 4 if (lockTaken) 5 { 6 try 7 { 8 //已經鎖定,想幹嗎就幹嗎吧 9 } 10 finally 11 { 12 Monitor.Exit(obj); 13 } 14 } 15 else 16 { 17 //沒有鎖定,當心嘍 18 }
Mutex【Mutual exclusion ,互 斥)是.Net Freamwork中 提供跨多個進程同步訪問的一個類 因爲系統能識別有名稱的互斥,因 此可 以使用 它禁止應用程序啓動兩次
1 static class Program 2 { 3 /// <summary> 4 /// 應用程序的主入口點。 5 /// </summary> 6 [STAThread] 7 static void Main() 8 { 9 bool createNew; 10 11 Mutex m = new Mutex(false, "test", out createNew); 12 if (!createNew) 13 { 14 MessageBox.Show("程序已啓動"); 15 Application.Exit(); 16 return; 17 } 18 Application.EnableVisualStyles(); 19 Application.SetCompatibleTextRenderingDefault(false); 20 Application.Run(new Form1()); 21 } 22 }