近期又看了一遍《C#高級編程》這本書,想對書中——任務、線程和同步這一章知識點做一個筆記,讓以後工作中忘記某個知識點能直接拿來用,在此進行一個總結。 Parallel數據和任務並行 一、Parallel.For 1、用Parallel.For並行運行迭代 static void ParallelFo... ...
近期又看了一遍《C#高級編程》這本書,想對書中——任務、線程和同步這一章知識點做一個筆記,讓以後工作中忘記某個知識點能直接拿來用,在此進行一個總結。
Parallel數據和任務並行
一、Parallel.For
1、用Parallel.For並行運行迭代
static void ParallelFor() { ParallelLoopResult result = Parallel.For(0, 10, item => { Console.WriteLine("{0},任務{1},線程{2}", item, Task.CurrentId, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10); }); Console.WriteLine("完成:{0}", result.IsCompleted); }
2、利用ParallelLoopState的Break()方法提前停止For迴圈
static void ParallelForBreak() { ParallelLoopResult result = Parallel.For(0, 10, (item, pls) => { Console.WriteLine("{0},任務{1},線程{2}", item, Task.CurrentId, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10); if (item > 5) pls.Break(); }); Console.WriteLine("完成:{0}", result.IsCompleted); Console.WriteLine("忽略:{0}", result.LowestBreakIteration); }
3、對每個線程進行初始化
static void ParallelForInit() { ParallelLoopResult result = Parallel.For<string>(0, 20, () => //Func<TLocal>僅對用於執行迭代的每個線程調用一次 { Console.WriteLine("初始化,任務:{0},線程:{1}", Task.CurrentId, Thread.CurrentThread.ManagedThreadId); return string.Format("線程:{0}", Thread.CurrentThread.ManagedThreadId); }, (item, pls, str1) => //Func<long, ParallelLoopState, TLocal, TLocal> 為迴圈體定義的委托 //第一個參數是迴圈迭代,第二個參數ParallelLoopState允許停止迴圈 //第三個參數接收初始化任務返回的值,類型是泛型For參數定義的 { Console.WriteLine("執行中,編號:{0},str1:{1},任務:{2},線程:{3}", item, str1, Task.CurrentId, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(10); return string.Format("編號:{0}", item); }, (str1) => //Action<TLocal> 這個方法僅對於每個線程調用一次,這是一個線程退出方法 { Console.WriteLine("完成:{0}", str1); }); }
4、取消任務
/// <summary> /// 取消任務 /// </summary> static void ParallelCancel() { CancellationTokenSource cts = new CancellationTokenSource(); //註冊一個任務取消完成時的委托 cts.Token.Register(() => { Console.WriteLine("任務已取消"); }); try { ParallelLoopResult result = Parallel.For(0, 10, new ParallelOptions() { CancellationToken = cts.Token }, item => { Console.WriteLine("迴圈:{0}", item); Thread.Sleep(1000); if (item == 5) cts.Cancel(); }); } catch (OperationCanceledException ex) { Console.WriteLine(ex.Message); } }
二、Parallel.ForEach
1、用Parallel.ForEach進行非同步遍歷
static void ParallelForeach() { string[] data = { "張三", "李四", "王五", "趙六", "錢七" }; ParallelLoopResult result = Parallel.ForEach(data, item => { Console.WriteLine("值:{0},任務:{1},線程:{2}", item, Task.CurrentId, Thread.CurrentThread.ManagedThreadId); }); }
2、利用ParallelLoopState的Break()方法中斷ForEach遍歷
static void ParallelForeachBreak() { string[] data = { "張三", "李四", "王五", "趙六", "錢七" }; ParallelLoopResult result = Parallel.ForEach(data, (item, pls, index) => { Console.WriteLine("值:{0},索引:{1},任務:{2},線程:{3}", item, index, Task.CurrentId, Thread.CurrentThread.ManagedThreadId); if (item == "王五") pls.Break(); }); }
三、Parallel.Invoke
1、通過Parallel.Invoke()方法調用多個方法,如果多個任務應並行運行,就可以使用Parallel.Invoke方法,它提供了任務並行性模式
static void ParallelInvoke() { Parallel.Invoke(Zhangsan, Lisi); } static void Zhangsan() { Console.WriteLine("張三"); } static void Lisi() { Console.WriteLine("李四"); }
Task任務
一、創建一個任務執行的方法。
static object taskMethodLock = new object(); static void TaskMethod(object title) { lock (taskMethodLock) { Console.WriteLine(title); Console.WriteLine("任務:{0},線程:{1}", Task.CurrentId == null ? -1 : Task.CurrentId, Thread.CurrentThread.ManagedThreadId); Console.WriteLine("線程池:{0}", Thread.CurrentThread.IsThreadPoolThread); Console.WriteLine("後臺線程:{0}", Thread.CurrentThread.IsBackground); Console.WriteLine(); } }
二、用不同方式創建任務
/// <summary> /// 用不同方式創建任務 /// </summary> static void CreateTask() { //方式1:使用實例化的TaskFactory類 TaskFactory tf = new TaskFactory(); Task task = tf.StartNew(TaskMethod, "使用TaskFactory類開始任務"); //方式2:使用Task類的靜態屬性Factory Task.Factory.StartNew(TaskMethod, "使用Task類的靜態屬性Factory開始任務"); //方式3:使用Task類的構造函數 Task task1 = new Task(TaskMethod, "使用Task類的構造函數開始任務"); task1.Start(); }
三、創建同步任務
/// <summary> /// 同步任務 /// </summary> static void SyncTask() { TaskMethod("主線程"); Task task = new Task(TaskMethod, "同步任務"); task.RunSynchronously(); }
四、創建長時間運行的任務
/// <summary> /// 創建長時間運行的任務 /// </summary> static void LongRunTask() { //創建一個新的線程,而不是使用線程池中的線程 Task task = new Task(TaskMethod, "長時間運行任務", TaskCreationOptions.LongRunning); task.Start(); }
五、任務的結果
/// <summary> /// 任務的結果 /// </summary> static void TaskResult() { Task<string> task = new Task<string>((name) => { Console.WriteLine(name); return string.Format("我叫:{0}", name); }, "張三"); task.Start(); Console.WriteLine(task.Result); task.Wait(); }
六、連續任務
/// <summary> /// 連續任務 /// </summary> static void ContinueTask() { Task task1 = new Task(() => { Console.WriteLine("任務開始:{0}", Task.CurrentId); Thread.Sleep(3000); }); //task1結束後會立即執行task2 Task task2 = task1.ContinueWith((task) => { Console.WriteLine("完成任務:", task.Id); Console.WriteLine("當前任務:", Task.CurrentId); Console.WriteLine("執行一些清理工作"); Thread.Sleep(3000); }); task1.Start(); }
七、任務層次結構
/// <summary> /// 父子任務 /// </summary> static void ParentAndChildTask() { Task parent = new Task(ParentTask); parent.Start(); Thread.Sleep(2000); Console.WriteLine(parent.Status); Thread.Sleep(4000); Console.WriteLine(parent.Status); } static void ParentTask() { Console.WriteLine("任務編號:", Task.CurrentId); Task child = new Task(ChildTask); child.Start(); Thread.Sleep(1000); Console.WriteLine("開始子任務"); } static void ChildTask() { Console.WriteLine("子任務"); Thread.Sleep(5000); Console.WriteLine("子任務完成"); }
八、取消任務
//取消任務 static void CancelTask() { CancellationTokenSource cts = new CancellationTokenSource(); cts.Token.Register(() => { Console.WriteLine("任務取消後執行"); }); Task task = new Task(() => { Console.WriteLine("開始任務"); for (int i = 0; i < 100; i++) { CancellationToken token = cts.Token; if (token.IsCancellationRequested) { Console.WriteLine("任務已取消"); token.ThrowIfCancellationRequested(); } Thread.Sleep(100); Console.WriteLine("迴圈編號:{0}", i); if (i == 5) cts.Cancel(); } }, cts.Token); task.Start(); try { task.Wait(); } catch (AggregateException ex) { Console.WriteLine("異常:{0},{1}", ex.GetType().Name, ex.Message); foreach (var innerException in ex.InnerExceptions) { Console.WriteLine("內部異常:{0},{1}", ex.InnerException.GetType().Name, ex.InnerException.Message); } } }
ThreadPool線程池
static void ThreadPoolDemo() { int workerThreads; int completionPortThreads; ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads); Console.WriteLine("工作線程:{0},IO線程:{1}", workerThreads, completionPortThreads); for (int i = 0; i < 5; i++) { ThreadPool.QueueUserWorkItem(TaskMethod); } } static void TaskMethod(object state) { for (int i = 0; i < 3; i++) { Console.WriteLine("迴圈:{0},線程:{1}", i, Thread.CurrentThread.ManagedThreadId); } }
Thread類
一、創建一個線程
/// <summary> /// 創建一個線程 /// </summary> static void CreateThread() { Thread thread = new Thread(() => { Console.WriteLine("這是一個線程,{0}", Thread.CurrentThread.ManagedThreadId); }); thread.Start(); Console.WriteLine("這是一個主線程,{0}", Thread.CurrentThread.ManagedThreadId); }
二、給線程傳遞數據
1、方式1,使用帶ParameterizedThreadStart委托參數的Thread構造函數
/// <summary> /// 給線程傳遞數據(方式1) /// </summary> static void ThreadWithData1() { var data = new { Message = "這是一條消息" }; Thread thread = new Thread((state) => { var obj = (dynamic)state; Console.WriteLine(obj.Message); }); thread.Start(data); }
2、方式2,創建一個自定義類,把線程的方法定義為實例方法,這樣就可以初始化實例的數據之後,啟動線程。
class MyThread { private string message = string.Empty; public MyThread(string message) { this.message = message; } public void ThreadMain() { Console.WriteLine(message); } }
/// <summary> /// 給線程傳遞數據(方式2) /// </summary> static void ThreadWithData2() { MyThread myThread = new MyThread("這是一條消息"); Thread thread = new Thread(myThread.ThreadMain); thread.Start(); }
三、後臺線程
/// <summary> /// 後臺線程 /// </summary> static void BackgroundThread() { Thread thread = new Thread(() => { Console.WriteLine("線程開始啟動:{0}",Thread.CurrentThread.Name); Thread.Sleep(3000); Console.WriteLine("線程完成啟動:{0}", Thread.CurrentThread.Name); }); thread.Name = "MyThread"; thread.IsBackground = true; thread.Start(); }