接著上期的文章繼續說異步與並行
並行來自於線程的方法實現,異步不一定。這句話,暈倒一大片程序員。
首先,多線程序是實現異步一種方法,兩者的共同目的:使主線程保持對用戶操作的實時響應,如點擊、拖拽、輸入字符等。使主程序看起來實時都保持著等待用戶響應的狀態,而後台卻有若干件事情在自己干。按消耗資源所在地可分為兩類:硬件異步類和CPU異步類。
硬件異步的特點:將需要在後台執行的操作甩給底層硬件去執行,不占用線程和CPU資源。所以說,並不是所有的異步都占有線程的。
硬件異步類大概有以下這幾類。
應用程序范圍
支持包含異步方法的 API
Web 訪問
HttpClient ,SyndicationClient
處理文件
StorageFile,StreamWriter,StreamReader,XmlReader
使用圖像處理
MediaCapture,BitmapEncoder,BitmapDecoder
WCF 編程
同步和異步操作
與套接字處理
Socket
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類基礎上做了實例論證,從而證明,後台線程的生命周期由依懶的前台線程結束而結束。
同時也將異步與多線程進一步舉例說明,線程只是異步的一種實現方法!
未完待續...