首先宣告 這是讀了 愉悅的紳士 文章
《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(); } }