接著上期的文章繼續說非同步與並行 並行來自於線程的方法實現,非同步不一定。這句話,暈倒一大片程式員。 首先,多線程式是實現非同步一種方法,兩者的共同目的:使主線程保持對用戶操作的實時響應,如點擊、拖拽、輸入字元等。使主程式看起來實時都保持著等待用戶響應的狀態,而後臺卻有若幹件事情在自己乾。按消耗資源所在地 ...
接著上期的文章繼續說非同步與並行
並行來自於線程的方法實現,非同步不一定。這句話,暈倒一大片程式員。
首先,多線程式是實現非同步一種方法,兩者的共同目的:使主線程保持對用戶操作的實時響應,如點擊、拖拽、輸入字元等。使主程式看起來實時都保持著等待用戶響應的狀態,而後臺卻有若幹件事情在自己乾。按消耗資源所在地可分為兩類:硬體非同步類和CPU非同步類。
硬體非同步的特點:將需要在後臺執行的操作甩給底層硬體去執行,不占用線程和CPU資源。所以說,並不是所有的非同步都占有線程的。
硬體非同步類大概有以下這幾類。
應用程式範圍 |
支持包含非同步方法的 API |
Web 訪問 |
|
處理文件 |
|
使用圖像處理 |
|
WCF 編程 |
|
與套接字處理 |
CPU常用的非同步方式、方法
1、獨立的線程—ThreadStart
一般情況下,要為不會阻止其他線程的相對較短的任務處理多個線程並且不需要對這些任務執行任何特定調度時,使用 ThreadPool 類是一種最簡單的方式。 但是,有多個理由創建您自己的線程:
如果您需要使一個任務具有特定的優先順序。
如果您具有可能會長時間運行(並因此阻止其他任務)的任務。
如果您需要將線程放置到單線程單元中(所有 ThreadPool 線程均處於多線程單元中)。
如果您需要與該線程關聯的穩定標識。 例如,您應使用一個專用線程來中止該線程,將其掛起或按名稱發現它。
如果您需要運行與用戶界面交互的後臺線程,.NET Framework 2.0 版提供了 BackgroundWorker 組件,該組件可以使用事件與用戶界麵線程的跨線程封送進行通信。
2、ThreadPool—ThreadPool.QueueUserWorkItem(M())
3、任務,Task系列--普通任務、關聯的任務(Task<T>.ContinueWith(…))、父子任務、任務工廠(TaskTactory<TResult>)
4、Parallel靜態類--- System.Threading.Tasks.Parallel.For(…) System.Threading.Tasks.Parallel.ForEach(…) Parallel.Invoke(() => Sort());
5、PLINQ
6、定時器
到此只是簡單的基礎知識闡述。如果不太清楚,下麵的陸續的文章將會一一講起。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
Thread類(線程類)
除了使用委托創建線程之外,還可以使用thread 類創建線程
static void Main(string[] args) { Thread t = new Thread(ThreadMain); t.Start(); Console.WriteLine("This ia a mian thread."); } static void ThreadMain() { Console.WriteLine("Running in a thread."); }
簡化以上代碼
static void Main(string[] args) { new Thread(() => Console.WriteLine("Running in a thread.") ).Start(); Console.WriteLine("This ia a mian thread."); }
再次見證 拉姆達(lambda)表達式與匿名方法 的威力。
在上面簡單的Thread類就創建並開始了一個線程。Thread類預設的是 IsBackground =false,也就是說它是前臺線程。
說到前臺線程與後臺線程,上一文章提到,當前臺進程停止的時候,後臺進程也將停止,當時是放在mian主線程測試的,必須關閉或結束主線程,看的不是太清楚
現在有了Thread類,下麵的例子將會開啟不依懶主線程的測試。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ConsoleApplication2 { class Program { static void Main(string[] args) { DateTime now = DateTime.Now; Thread t1 = new Thread(() => { Console.WriteLine("Running in a thread t1."); Func<decimal, int, decimal> f = (money, ms) => { Console.WriteLine("SaveBankAccountPersonA thread started! current run at threadID:" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine("SaveBankAccountPersonA thread IsBackground " + Thread.CurrentThread.IsBackground); Thread.Sleep(ms); Console.WriteLine("SaveBankAccountPersonA thread completed!"); return ++money; }; var ar = f.BeginInvoke(1, 200, (r) => { if (r == null) { throw new ArgumentNullException("r"); } Thread.Sleep(1000); Console.WriteLine("AsycyCallBackCurrentMoneyPersonA:{0}", f.EndInvoke(r)); Console.WriteLine("AsycyCallBackRunTimePersonA:{0}", (DateTime.Now - now).TotalSeconds); Console.WriteLine("AsycyCallBackSaveBankAccountPersonA thread IsBackground " + Thread.CurrentThread.IsBackground); }, null); while (!ar.IsCompleted) { Console.WriteLine("threadT1 wating current run at treadID:" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(50); } }); Thread t2 = new Thread(() => { Console.WriteLine("Running in a thread t2."); Func<decimal, int, decimal> f = (money, ms) => { Console.WriteLine("SaveBankAccountPersonB thread started! current run at threadID:" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine("SaveBankAccountPersonB thread IsBackground " + Thread.CurrentThread.IsBackground); Thread.Sleep(ms); Console.WriteLine("SaveBankAccountPersonB thread completed!"); return ++money; }; var ar = f.BeginInvoke(1, 200, (r) => { if (r == null) { throw new ArgumentNullException("r"); } Console.WriteLine("AsycyCallBackCurrentMoneyPersonB:{0}", f.EndInvoke(r)); Console.WriteLine("AsycyCallBackRunTimePersonB:{0}", (DateTime.Now - now).TotalSeconds); Console.WriteLine("AsycyCallBackSaveBankAccountPersonB thread IsBackground " + Thread.CurrentThread.IsBackground); }, null); while (!ar.IsCompleted) { Console.WriteLine("threadT2 wating current run at treadID:" + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(50); } }); t1.Start(); t2.Start(); t1.Abort(); Console.WriteLine("This ia a mian thread."); Console.ReadKey(); } } }
上面的代碼很多,對上一文章的進行擴展。分別啟用兩個線程t1和t2 ,併在每個線程裡加入非同步委托A和B,從而開始新的後臺線程(非同步委托預設是後臺線程)
上面這張圖,t1還沒有來及運行,就已停止,下麵這張圖,t1運行起來了
但是A還是沒有運行起來,充分說明A一併被t1停止
後臺線程A和B 同時擁有兩個回調函數 ,在A回調函數里 加入了Sleep(1000) 延遲1秒,緊接著外面t1.Abort();結束前臺線程t1。從而達到,外面的t1前臺線程結束時後臺線程A還沒有來及結束(實際上已強制性並隨t1前臺線程結束了!)
這樣就驗證了,前臺線程結束所依懶的後臺線程所並隨結束的事實!代碼就是最好的說明
小結:本節在文章一節中著重闡述的前臺線程與後臺線程作了在Thread類基礎上做了實例論證,從而證明,後臺線程的生命周期由依懶的前臺線程結束而結束。
同時也將非同步與多線程進一步舉例說明,線程只是非同步的一種實現方法!
未完待續...