首先聲明 這是讀了 愉悅的紳士 文章 《菜鳥之旅——學習線程(線程和線程池)》 《Task與線程》 的一些個人總結,還是那句話,如有不對,歡迎指正 文章以代碼加註釋的方法展示。 //線程的創建,阻塞和同步 //對方法加鎖 //線程池 //Task 任務 推薦使用任務來做多線程的,便於管理 ...
首先聲明 這是讀了 愉悅的紳士 文章
《Task與線程》
的一些個人總結,還是那句話,如有不對,歡迎指正
文章以代碼加註釋的方法展示。
//線程的創建,阻塞和同步
public static ManualResetEvent MREstop=new ManualResetEvent(false); public static AutoResetEvent AREstop = new AutoResetEvent(false); static void Main(string[] args) { //使用方法註冊 Thread Thread1 = new Thread(Method1); //使用Lambda註冊 Thread Thread2 = new Thread((s) => { //暫停線程2,使用ManualResetEvent暫停,當使用Set方法的時候會跳過所有WaitOne(); //MREstop.WaitOne(); //暫停主線程,使用AutoResetEvent暫停,當使用Set方法的時候會跳過第一次遇到的WaitOne(); AREstop.WaitOne(); Console.WriteLine("----這是帶參數方法2,參數為{0}----",s); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法2結束----"); }); //若直接運行,會發現,Thread1和主線程的代碼會交錯在一起,而Thread2的代碼一直在最後出現,這是因為Thread1和主線程一起運行,而Thread2延遲運行 Thread1.Start(); Thread2.Start("這是一個參數"); //取消註釋,會發現Thread1和Thread2都執行完後,才會執行主線程代碼 //Thread1.Join(); //Thread2.Join(); //暫停主線程,使用ManualResetEvent暫停,當使用Set方法的時候會跳過所有WaitOne(); //MREstop.WaitOne(); //暫停主線程,使用AutoResetEvent暫停,當使用Set方法的時候會跳過第一次遇到的WaitOne(); //AREstop.WaitOne(); Console.WriteLine("----這是主線程----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----主線程結束----"); } static void Method1() { Thread.Sleep(1000); Console.WriteLine("----這是不帶參數方法1----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法1結束----"); //使用線程1開啟同步,當使用Set方法的時候會跳過所有WaitOne(); //MREstop.Set(); //使用線程1開啟同步,,當使用Set方法的時候會跳過第一次遇到的WaitOne(),所以主要是看Cpu先執行那個進程; //AREstop.Set(); }
//對方法加鎖
static readonly object LockObject = new object(); static int i = 100; static void Main(string[] args) { //實例化100條線程,執行同一個方法 for (int i = 0; i < 100; i++) { Thread Thread1 = new Thread(Method1); Thread1.Start(); } } static void Method1() { //若不加鎖,所有線程都可以同時訪問該方法,會造成顯示的結果混亂,而加了鎖,就同時只能擁有一個線程訪問該方法 //Monitor.Enter(LockObject); //i++非原子性操作,可能同時被多個線程執行,造成競態,會影響運算結果,所以不能在多線程中使用。 //i++; //推薦使用線程原子性自增操作 System.Threading.Interlocked.Increment(ref i); Thread.Sleep(10); Console.WriteLine("This is Thread{0} and i={1}", Thread.CurrentThread.ManagedThreadId, i); Console.WriteLine("--------------------------------"); //加了鎖必須解鎖 //Monitor.Exit(LockObject); //或者使用lock(LockObject)的方法,相當於try{Monitor.Enter(LockObject);}catch{}finally{Monitor.Exit(LockObject);}的簡便寫法 //lock(LockObject) //{ // System.Threading.Interlocked.Increment(ref i); // Thread.Sleep(10); // Console.WriteLine("This is Thread{0} and i={1}", Thread.CurrentThread.ManagedThreadId, i); // Console.WriteLine("--------------------------------"); //} }
//線程池
public static AutoResetEvent AREstop1 = new AutoResetEvent(false); static void Main(string[] args) { AutoResetEvent AREstop2 = new AutoResetEvent(false); //創建並且執行,線程池上限為CPU核心數*250,預設為後臺線程 ThreadPool.QueueUserWorkItem(new WaitCallback(Method1), AREstop2); //創建並且執行 ThreadPool.QueueUserWorkItem(new WaitCallback(s => { Thread.Sleep(2000); Console.WriteLine("----這是帶參數方法2,參數為{0}----", s); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法2結束----"); AREstop1.Set(); }), "這是一個參數"); //線程池的同步線程和線程一致,可以使用ManualResetEvent和AutoResetEvent執行。 //由於線程池沒有Join方法,所以可以使用WaitAll()方法來達到所有線程執行完畢後執行主線程的效果 List<WaitHandle> handles = new List<WaitHandle>(); handles.Add(AREstop1); // handles.Add(AREstop2); //註意,對多個線程要使用不同的AutoResetEvent,只要數組中的AutoResetEvent接受到set指令就解鎖,若全部為同一個名字 //則只要任何一個進程set之後,就會執行主線程。由於線程池預設為後臺線程,一旦執行完成主線程,則其餘線程自動結束 //必須數組之中的AutoResetEvent全部set後才會執行,如果該有一個沒有set,都不會執行主線程。 //WaitAll最大數組上限為64 WaitHandle.WaitAll(handles.ToArray()); Console.WriteLine("----這是主線程----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----主線程結束----"); } //方法要帶一個參數 static void Method1(object obj) { Thread.Sleep(1000); Console.WriteLine("----這是帶參數方法1----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法1結束----"); AutoResetEvent AREstop2 = (AutoResetEvent)obj ; AREstop2.Set(); }
//Task 任務 推薦使用任務來做多線程的,便於管理
public static AutoResetEvent AREstop1 = new AutoResetEvent(false); static void Main(string[] args) { //Task實例化的都是後臺線程,如果要更改為前臺線程,需要再方法裡面修改 #region Task任務 使用線程池 //{ // //實例化任務,必須手動啟動,註意,方法是不能帶參數的 // Task TaskFirst = new Task(Method1); // //Status可以標識當前任務的狀態 // //Created:表示預設初始化任務,但是“工廠創建的”實例直接跳過。 // //WaitingToRun: 這種狀態表示等待任務調度器分配線程給任務執行。 // //RanToCompletion:任務執行完畢。 // Console.WriteLine("TaskFirst的狀態:{0}", TaskFirst.Status); // TaskFirst.Start(); // Console.WriteLine("TaskFirst的狀態:{0}", TaskFirst.Status); // //工廠創建的直接執行 // Task TaskSecond = Task.Factory.StartNew(() => // { // Console.WriteLine("----這是不帶參數方法2----"); // Console.WriteLine(DateTime.Now); // Console.WriteLine("----方法2結束----"); // }); // //使用這種方法刪除任務 // //CancellationTokenSource cancelTokenSource = new CancellationTokenSource(); // //Task.Factory.StartNew(() => // //{ // // Console.WriteLine("----這是要刪除方法4----"); // // Console.WriteLine(DateTime.Now); // // Console.WriteLine("----要刪除方法結束----"); // //}, cancelTokenSource.Token); // //cancelTokenSource.Cancel(); // //流程式控制制 // { // //沒有加標識的預設使用線程池創建,若主線程結束自動結束,所以需要先堵塞主線程 // //AREstop1.WaitOne(); // //或者使用阻塞 // Task.WaitAll(TaskFirst, TaskSecond); // //也可以使用Wait()等待單個線程,你會發現下麵TaskFirst的狀態的狀態為Running,因為主線程開始運行了,而線程TaskFirst還在運行中 // //TaskSecond.Wait(); // //Task.WaitAny 只要數組中有一個執行完畢,就繼續執行主線程 // //Task.WaitAny(TaskFirst, TaskSecond); // //繼續執行,在TaskFirst任務結束後繼續執行,此時TaskFirst已經結束。記得加Wait(),否則主線程結束就直接結束了。 // TaskFirst.ContinueWith(NewTask => // { // Console.WriteLine("----這是不帶參數方法3----"); // Console.WriteLine(DateTime.Now); // Console.WriteLine("TaskFirst的狀態:{0}", TaskFirst.Status); // Console.WriteLine("----方法3結束----"); // }).Wait(); // } // Console.WriteLine("TaskFirst的狀態:{0}", TaskFirst.Status); //} #endregion #region Task任務 使用線程 { ////實例化任務,必須手動啟動,註意,方法是不能帶參數的 //Task TaskFirst = new Task(Method1, TaskCreationOptions.LongRunning); //TaskFirst.Start(); } #endregion #region Task任務 帶參數 { Task<int> TaskFirst = new Task<int>(((x) => { return (int)(x); }), 10); TaskFirst.Start(); Console.WriteLine(" result ={0}", TaskFirst.Result); Task<string> TaskSecond = Task<string>.Factory.StartNew(new Func<object, string>(x => { return $"This is {x}"; }), 10); Console.WriteLine(" result ={0}", TaskSecond.Result); } #endregion Console.WriteLine("----這是主線程----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----主線程結束----"); } //C# 6.0只讀賦值 static object Locker { get; } = new object(); static void Method1() { lock (Locker) { Thread.CurrentThread.IsBackground = false; Thread.Sleep(1000); Console.WriteLine("----這是帶參數方法1----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法1結束----"); //AREstop1.Set(); } }