首先聲明 這是讀了 愉悅的紳士 文章html
《Task與線程》多線程
的一些我的總結,仍是那句話,若有不對,歡迎指正學習
文章以代碼加註釋的方法展現。spa
//線程的建立,阻塞和同步線程
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(); }
//對方法加鎖code
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("--------------------------------"); //} }
//線程池htm
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 任務 推薦使用任務來作多線程的,便於管理blog
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(); } }