本文歡迎轉載,原文地址:http://www.cnblogs.com/DjlNet/p/7522869.htmlhtml
咱們接了上一篇的未完待續,接着咱們的劃重點之行.....哈哈web
通俗的來說就是先把數據準備好也就是取數據的邏輯已經編寫好了(也就是構建好了IEumerable
就短短的幾行代碼便實現了對大量日誌的檢索、解析過濾,這得感謝LINQ的流式處理。關於上述代碼的紅框部分(1)Directory.GetFiles
以及 Directory.EnumerateFiles
兩個API之間的區別看名字就一目瞭然了吧,這裏引用一下官方回答:數據庫
The EnumerateFiles and GetFiles methods differ as follows: When you use EnumerateFiles, you can start enumerating the collection of names before the whole collection is returned; when you use GetFiles, you must wait for the whole array of names to be returned before you can access the array. Therefore, when you are working with many files and directories, EnumerateFiles can be more efficient.編程
(2)File.ReadLines
與 File.ReadAllLines
之間的區別從返回值也能夠明顯的分別出來了,一個是流式加載一個當即加載,道理如同上述的第一點(1)
這樣一來也一樣說明了,爲何框架老是儘可能嘗試以一種流式的方式處理數據集,這也是爲何咱們須要返回IEnumerable
這裏呢,園子不少好文章都已經解釋了怎麼用呀,什麼大體原理,什麼狀態機什麼的,我這裏就仍是引用書中的說辭來通俗的說說,在沒有C#5這麼安逸的異步編程以前以後的帶來的感覺,舉個小例子看C#團隊幫咱們幹了什麼好事兒。
不用在乎界面,下面把完整代碼貼出來:架構
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApp1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); _synchronizationContext = SynchronizationContext.Current; } private static readonly HttpClient _httpClient = new HttpClient(); private static readonly WebClient _webClient = new WebClient(); private readonly SynchronizationContext _synchronizationContext; private const string _url = "http://www.bing.com"; /// <summary> /// ThreadPool方式構建異步 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { this.button1.Enabled = false; ThreadPool.QueueUserWorkItem(x => { try { var result = _webClient.DownloadString(_url); _synchronizationContext.Post(length => { int temp = Convert.ToInt32(length); this.label4.Text = temp.ToString(); }, result.Length); } catch (Exception exception) { // 這裏通過測試可使用靜態方法Show,原理應該也是把消息寫進Winform的消息泵中,由WinForm框架自身去循環調度觸發 MessageBox.Show(exception.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error); // 同理下面的代碼依然能夠 //_synchronizationContext.Post(msg => //{ // var message = msg as string; // MessageBox.Show(message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error); //}, exception.Message); } finally { // 測試除UI線程以外的線程訪問UI控件異常 //this.button1.Enabled = true; _synchronizationContext.Post(empty => { this.button1.Enabled = true; }, null); } }); } /// <summary> /// Task構建異步 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click(object sender, EventArgs e) { this.button2.Enabled = false; Task.Factory.StartNew<string>(x => { var result = _webClient.DownloadString(_url); return result.Length.ToString(); }, null) .ContinueWith(task => { if (task.IsFaulted) { // faulted with exception Exception ex = task.Exception; while (ex.InnerException != null) ex = ex.InnerException; MessageBox.Show("Error: " + ex.Message); } else if (task.IsCanceled) { // this should not happen // as you don't pass a CancellationToken into your task MessageBox.Show("Canclled."); } else { // completed successfully if (!string.IsNullOrWhiteSpace(task.Result)) { _synchronizationContext.Post(length => { int temp = Convert.ToInt32(length); this.label3.Text = temp.ToString(); }, task.Result); } //MessageBox.Show("Result: " + task.Result); } _synchronizationContext.Post(empty => { this.button2.Enabled = true; }, null); //// 測試除UI線程以外的線程訪問UI控件異常 ////this.button2.Enabled = true; }, TaskContinuationOptions.ExecuteSynchronously); } /// <summary> /// Async/Await構建異步 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private async void button3_Click(object sender, EventArgs e) { this.button3.Enabled = false; try { string temp = await _httpClient.GetStringAsync(_url); this.label6.Text = temp.Length.ToString(); } catch (Exception exception) { while (exception.InnerException != null) { exception = exception.InnerException; } MessageBox.Show(exception.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { this.button3.Enabled = true; } } } }
其中這裏還需對上面代碼 SynchronizationContext 說明一下:正是由於有了它,咱們的異步async/await異步函數的後續操做,可以正確的回到UI線程執行(前提是ConfigureAwait(continueOnCaptureedContext : true) 顯示的捕獲調用者的上下文這裏就是UI線程上下文,其中該方法默認也是參數:true ),其實這個玩意兒已經在.NET 2.0都已經有了,當時是爲了提供給 BackgroundWork等組件使用,SynchronizationContext 保證了在適當的線程執行委託的概念,以致於咱們調用 SynchronizationContext.Post(異步)或者 SynchronizationContext.Send (同步) 發送消息,與在 Winform中的 Control.BeginInvoke和Control.Invoke有殊途同歸之妙呀!!!得注意一點就是在不一樣的環境模型中,同步上下文表明的含義是不一致的咯!!!這裏的咱們代碼中的 SynchronizationContext 就表明了 UI線程的上下文信息。app
接着咱們經過上面的代碼,看到變化點來看到好處以及C#關於異步編程怎麼進化的哈,其中採用了三種不一樣的方式實現同一種需求,單從代碼量上面或者複雜度來講都是遞減的(由於這裏環境是Winform因此要遵循兩個原則:一、不要在UI線程上執行耗時的操做 二、不要除了UI線程以外的其餘線程訪問UI控件,因此代碼略多了點),不過從理解上面都仍是比較好理解,畢竟代碼上面你們一看就應該是知道底層套路都同樣,可是從體驗或者感覺其中包含了異常處理、線程切換自動回到正確的上下文等仍是Async/Await的方式最舒服,雖然到了Task的時候有ContinueWith來銜接任務能夠解決回調地獄的問題(畢竟ThreadPool可憐的尚未回調機制)。框架
await 的主要目的是等待耗時操做是能夠避免阻塞,當方法執行到 await 表達式就返回了,當完成等待以後,後續操做依然能夠回到原先的UI線程去執行,看到這裏有木有一種感受就是async/await已經幫咱們把咱們本身的手動實現都作好了並且作得更好作得更多,那是由於C#編譯器會對全部await都構建一個後續操做,這裏後續操做對於咱們來就是就是this.label6.Text = temp.Length.ToString();
。關於更加詳細的解讀,以及內部狀態機的構造和狀態管理等就是比較複雜了,這裏博主也不是很清楚,詳情參考官方文檔或者博客唄以及書中的詳解篇幅也是有的,其實通常狀況也不須要關心內部構造,須要關心如何去最佳實踐便可。異步
到這裏第二篇文章也差很少了,這本書的劃重點也差很少了(我的來看的話,其實呢可能還有其餘忽略的地方,後面CLR溫故的時候再補充也是能夠的),其實再看了第二遍這本書吶,給我最大感覺仍是對書中某些模棱兩可的知識可能更加稍微掌握了些,還有就是在C#發展的里程碑中,在功能性和體驗性上面來講,我的以爲仍是 LINQ、Async/Await 帶來的東西是給開發者最好的禮物,簡直就是其餘語言模仿或者學習的標杆(原諒博主活在C#的溫柔鄉中......),哈哈,固然了好的語言設計那確定是要分享的嘛,否則其餘開發者豈不是很難受!!並且在後面的C#6中對異步編程的await關鍵字作了進一步提高,具體參考微軟文檔。好了,重點來了,接下來博主吶,就會開始研究框架框架框架(其實也一直有關注和學習,只是感受不能出文記錄),注意是框架而不是架構哦,畢竟架構自己也是由不少框架組建起來的哦就好像基礎組建與微服務的關係同樣,主要是看看人家怎麼設計框架的,而後纔是代碼是怎麼寫的.....。最後再說一句:掌控本身,就是掌控敵人 --盲僧 !!!async
/// <summary> /// Task構建異步優化 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button4_Click(object sender, EventArgs e) { this.button4.Enabled = false; var task1 = Task.Factory.StartNew<string>(x => { var result = _webClient.DownloadString(_url); return result.Length.ToString(); }, null); var task2 = task1.ContinueWith(task => { if (task.IsFaulted) { // faulted with exception Exception ex = task.Exception; while (ex.InnerException != null) ex = ex.InnerException; MessageBox.Show("Error: " + ex.Message); } else if (task.IsCanceled) { // this should not happen // as you don't pass a CancellationToken into your task MessageBox.Show("Canclled."); } else { // completed successfully if (!string.IsNullOrWhiteSpace(task.Result)) { this.label8.Text = task.Result; } } this.button4.Enabled = true; }, TaskScheduler.FromCurrentSynchronizationContext()); }
優化說明:刪除手動使用同步上下文去控制UI元素,而使用了關鍵的TaskScheduler.FromCurrentSynchronizationContext()
來自動使用當前的同步上下文,方法說明:建立一個與當前 System.Threading.SynchronizationContext 關聯的 System.Threading.Tasks.TaskScheduler,其實折騰這玩意兒爲了啥,也就是爲了也能在.Net4.0的環境也就是客戶端電腦還處於這個時期的時候,可以正確是姿式編寫異步代碼且不那麼難受就行了,至於說可使用一個nuget包Microsoft.Bcl.Async
還沒有嘗試過,道聽途說有點小問題沒親測,不過目前來看應該還能夠(瞎猜),主要是客戶端的電腦人家是win7安裝默認也是net4.0,可是吶他們又不想卡主界面致使未響應,其實也是數據庫和網絡(異地跨國調用,攤手.jpg)不給力致使的,好了該睡覺了.....晚安!老鐵們....