YA! 第四篇!! :D 還是一樣要先感謝一下編輯賞光,讓我有點空間寫些不一樣的東西。
基本的執行緒相關的程式設計跟函式庫,講的差不多了,其實這些也沒什麼好寫的。接下來打算寫一些應用的模式,來談談有那些方法,那些設計方式才能夠有效的發揮多執行緒的優點。看了 .NET Framework 4.0 / Visual Studio 2010 的 ROADMAP,有一大部份的重點擺在平行處理,INTEL年底也要發表四核 + HT 的 CPU ( WINDOWS 會認為有八個處理器 ),軟硬體都備齊了,剩下的就是程式設計師的巧思了。
其實之前貼過幾篇類似主題的文章,只是這次把它統合起來介紹一下。生產線模式,如果簡化後就是 [生產者消費者] 的模式,而把它徹底一點的應用,則是上回提到 [Stream Pipeline] ..
這篇也是第一次在雜誌上嘗試說明比較偏設計概念的文章,實作比較少,很怕不合讀者的口味... 應該不會貼了就沒續篇了吧? :P 有買雜誌的記得讀者回函填一下,哈哈,也算是點鼓勵。這次範例程式也是 Console application (我不會寫太炫的程式 :P ),需要的可以點 [這裡] 下載!
哈哈,這篇拖的夠久了 :P 上篇扯太多,寫到一半寫不完就留到這篇了。寫出可靠的程式,這是軟體工程師的基本要求。上篇提到了 TRACE / ASSERT 的應用,來複習一下: TRACE: 原本是 C 的除錯用巨集,目的是用適合的方式輸出除錯用的訊息,用來跟一般的訊息輸出有所區別。因為用的是不同的方式輸出,可以很容易的統一關掉。隨著工具的進步,輸出的方式也越來越適合除錯,比如輸出到開發工具的除錯視窗,或是輸出成記錄檔等等。 ASSERT: 也是除錯用巨集,它接受一個 bool 參數,輸入值為 TRUE 時一切正常,就像沒呼叫一樣,輸入 FALSE 則會中斷程式,或是輸出顯目的警告訊息。目的在於確保程式的每個步驟情況都如預料般的順利。 這兩個東西從 C 的巨集,衍生出各種語言及環境都有各自的版本。它的目的很簡單,就是 [Writing Solid Code] 裡提到的: 用同一套程式碼,同時維護兩個版本 (RELEASE / DEBUG),讓錯誤自動跑出來 雖然這本書提到了不少技巧,正確的應用 TRACE / ASSERT 是最基本的。但是那些細節並不是主要的重點。重點是你在寫 CODE 時有時時刻刻記得要盡量減少 BUG 嗎? 你有正確的擬出對策嗎? 來看看上回最後一段範例程式:
public static int ComputeQuestionScore(XmlElement quiz_question, XmlElement paper_question) { int totalScore = 0; int itemCount = quiz_question.SelectNodes("item").Count; Trace.Assert(quiz_question != null); Trace.Assert(paper_question != null); Trace.Assert(paper_question.SelectNodes("item").Count == quiz_question.SelectNodes("item").Count); // // 如果都沒作答, 此題放棄 // if (paper_question.SelectNodes("item[@checked='true']").Count == 0) { return 0; } // // 題目的配分 // int quiz_score = int.Parse(quiz_question.GetAttribute("score")); // // 答對一個選項的分數 // int item_score = quiz_score / itemCount; for (int itemPos = 0; itemPos < itemCount; itemPos++) { XmlElement quiz_item = quiz_question.SelectNodes("item")[itemPos] as XmlElement; XmlElement paper_item = paper_question.SelectNodes("item")[itemPos] as XmlElement; // // 算成積 // if (quiz_item.GetAttribute("correct") == paper_item.GetAttribute("checked")) { totalScore += item_score; } else { totalScore -= item_score; } } Trace.Assert(totalScore >= (0 - quiz_score)); Trace.Assert(totalScore <= quiz_score); return totalScore; }
Trace.Assert(quiz_question != null); Trace.Assert(paper_question != null); Trace.Assert(paper_question.SelectNodes("item").Count == quiz_question.SelectNodes("item").Count);
Trace.Assert(totalScore >= (0 - quiz_score)); Trace.Assert(totalScore <= quiz_score); return totalScore;
哈哈,貼一下家裡魚缸的照片... 家裡養的孔雀魚一直生就算了,無意間丟進來的一隻蝸牛,沒兩個月竟然也生了一堆,現在算算大概有四十隻吧 @_@,照片裡紅紅的都是...
不過有了蝸牛 (消費者),把水裡的魚大便跟水藻都吃的乾乾淨淨的也不錯啦,以前每週要換一次水,現在偷懶撐久一點都無所謂了 :D
不知道是更新了啥 PATCH,還是那次沒正常關機,我公司 VISTA 的 UAC 突然莫名奇妙的被打開了。怪的是控制台裡看到的還是關掉的,不管怎麼改狀態也不會改變 (一直都是關的) ...。
直覺告訴我一定是控制台的 AP 那邊出問題,設定值寫不進去造成的...,於是我就開使找其它可以修改 UAC 設定的方法...,最後找到這個,還真的成功了 :D,看來沒機會動用 ProcessMonitor 追追看問題了..
找到的方法是: msconfig.exe
在開始 --> 執行裡輸入 msconfig.exe 後,可以看到這一項:
看來是直接修改 registry, 果然有效,直接執行後 REBOOT 就一切正常了 -_-, 如果有人也碰過一樣的問題可以試看看!
撐了很久,續篇來了。這次要進階一點,直接從 software engineer (軟體工程師) 的階段開始。 所謂的軟體工程師,我對它的定義是在這個領域已經算是資深人員了。programmer 該作的是把程式寫好,要挑正確的方式及技術寫好你的程式 (如之前幾篇介紹的演算法及資料結構等等)。而軟體工程師呢? 之前介紹的那些已經不夠了,你該好好的安排你的 code 及工具,要能把你的 solution (如會用到的演算法及資料結構),跟你手上能運用的資源 (如程式語言、開發工具及函式庫) 作最佳化的搭配及整合。因此我認為在這階段的重點有幾個:
<?xml version="1.0" encoding="utf-8" ?> <quiz> <question score="20"> <body>那一隻熊最勵害?</body> <item correct="false">白熊</item> <item correct="false">黑熊</item> <item correct="false">棕熊</item> <item correct="true">灰熊</item> </question> <question score="40"> <body>誰發現萬有引力?</body> <item correct="false">鼠頓</item> <item correct="true">牛頓</item> <item correct="false">虎頓</item> <item correct="false">兔頓</item> </question> <question score="40"> <body>下列那些東西是可以吃的?</body> <item correct="false">東瓜</item> <item correct="true">西瓜</item> <item correct="true">南瓜</item> <item correct="false">北瓜</item> </question> </quiz>
<?xml version="1.0" encoding="utf-8" ?> <quiz> <question> <item checked="false" /> <item checked="false" /> <item checked="false" /> <item checked="true" /> </question> <question> <item checked="false" /> <item checked="true" /> <item checked="false" /> <item checked="false" /> </question> <question> <item checked="false" /> <item checked="true" /> <item checked="true" /> <item checked="false" /> </question> </quiz>而我交待的算分規則也很簡單,就一般考試的計算方式: 每題有自己的配分,以複選題來算,答對幾個選項就照比例給分,答錯會倒扣。新人工程師果然好用耐操,不一會就交給我這份 Library 的程式碼:
public static int ComputeQuizScore(XmlDocument quizDoc, XmlDocument paperDoc) { int questionCount = quizDoc.SelectNodes("/quiz/question").Count; int totalScore = 0; for (int questionPos = 0; questionPos < questionCount; questionPos++) { XmlElement quiz_question = quizDoc.SelectNodes("/quiz/question")[questionPos] as XmlElement; XmlElement paper_question = paperDoc.SelectNodes("/quiz/question")[questionPos] as XmlElement; totalScore += ComputeQuestionScore(quiz_question, paper_question); } return totalScore; } public static int ComputeQuestionScore(XmlElement quiz_question, XmlElement paper_question) { int totalScore = 0; int itemCount = quiz_question.SelectNodes("item").Count; // // 題目的配分 // int quiz_score = int.Parse(quiz_question.GetAttribute("score")); // // 答對一個選項的分數 // int item_score = quiz_score / itemCount; for (int itemPos = 0; itemPos < itemCount; itemPos++) { XmlElement quiz_item = quiz_question.SelectNodes("item")[itemPos] as XmlElement; XmlElement paper_item = paper_question.SelectNodes("item")[itemPos] as XmlElement; // // 算成積 // if (quiz_item.GetAttribute("correct") == paper_item.GetAttribute("checked")) { totalScore += item_score; } else { totalScore -= item_score; } } return totalScore; }
<?xml version="1.0" encoding="utf-8" ?> <quiz> <question> <item checked="false" /> <item checked="false" /> <item checked="false" /> <item checked="true" /> </question> <question> <item checked="false" /> <item checked="false" /> <item checked="false" /> <item checked="false" /> </question> <question> <item checked="false" /> <item checked="false" /> <item checked="false" /> <item checked="false" /> </question> </quiz>
public static int ComputeQuestionScore(XmlElement quiz_question, XmlElement paper_question) { int totalScore = 0; int itemCount = quiz_question.SelectNodes("item").Count; // // 如果都沒作答, 此題放棄 // if (paper_question.SelectNodes("item[@checked='true']").Count == 0) return 0; // // 題目的配分 // int quiz_score = int.Parse(quiz_question.GetAttribute("score"));
<?xml version="1.0" encoding="utf-8" ?> <quiz> <question> <item checked="false" /> <item checked="false" /> <item checked="false" /> <item checked="false" /> </question> <question> <item checked="false" /> <item checked="false" /> <item checked="false" /> <item checked="false" /> </question> <question> <item checked="true" /> <item checked="false" /> <item checked="false" /> <item checked="true" /> </question> </quiz>果然有柯南的地方就有密室殺人事件... @_@,又被我抓到一個問題。這次得到的總分是 -40,那有人扣到負的? 工程師又被我叫來唸了一頓,這次改了這版程式給我 (第十一行,最低是0分):
public static int ComputeQuizScore(XmlDocument quizDoc, XmlDocument paperDoc) { int questionCount = quizDoc.SelectNodes("/quiz/question").Count; int totalScore = 0; for (int questionPos = 0; questionPos < questionCount; questionPos++) { XmlElement quiz_question = quizDoc.SelectNodes("/quiz/question")[questionPos] as XmlElement; XmlElement paper_question = paperDoc.SelectNodes("/quiz/question")[questionPos] as XmlElement; totalScore += ComputeQuestionScore(quiz_question, paper_question); } return Math.Max(0, totalScore); }金融業最重視的就是錢了,銀行的程式連一毛錢都不能算錯,而在線上考試的系統也一樣,連一分都不能算錯。只是當你的老闆這樣要求你的時後,你是謹記在心,還是照一般方式寫程式嗎? 還是你有什麼有效的措施可以預防這些問題? 這時才是顯示你專業的地方啊... 套句鄉民的慣用語:
public static int ComputeQuestionScore(XmlElement quiz_question, XmlElement paper_question) { int totalScore = 0; int itemCount = quiz_question.SelectNodes("item").Count; if (quiz_question == null) { throw new Exception("沒有題目卷"); } if (paper_question == null) { throw new Exception("沒有答案卷"); } // // 如果都沒作答, 此題放棄 // if (paper_question.SelectNodes("item[@checked='true']").Count == 0) { Console.WriteLine("偵測到沒作答的答案,此題放棄"); return 0; } // // 確認題目跟答案的選項數目一致 // if (paper_question.SelectNodes("item").Count != quiz_question.SelectNodes("item").Count) { throw new Exception("此題的選項跟題目定義不符合"); }老實說這範例我也寫不下去了,加這麼多 check 是好事,不過事情都有黑暗面,我覺的不妥的地方有幾個:
public static int ComputeQuizScore(XmlDocument quizDoc, XmlDocument paperDoc) { Trace.Assert(quizDoc != null); Trace.Assert(paperDoc != null); Trace.Assert(quizDoc.SelectNodes("/quiz/question").Count == paperDoc.SelectNodes("/quiz/question").Count); int questionCount = quizDoc.SelectNodes("/quiz/question").Count; int totalScore = 0; for (int questionPos = 0; questionPos < questionCount; questionPos++) { XmlElement quiz_question = quizDoc.SelectNodes("/quiz/question")[questionPos] as XmlElement; XmlElement paper_question = paperDoc.SelectNodes("/quiz/question")[questionPos] as XmlElement; totalScore += ComputeQuestionScore(quiz_question, paper_question); } totalScore = Math.Max(0, totalScore); Trace.Assert(totalScore >= 0); return totalScore; } public static int ComputeQuestionScore(XmlElement quiz_question, XmlElement paper_question) { int totalScore = 0; int itemCount = quiz_question.SelectNodes("item").Count; //if (quiz_question == null) //{ // throw new Exception("沒有題目卷"); //} //if (paper_question == null) //{ // throw new Exception("沒有答案卷"); //} //// //// 確認題目跟答案的選項數目一致 //// //if (paper_question.SelectNodes("item").Count != quiz_question.SelectNodes("item").Count) //{ // throw new Exception("此題的選項跟題目定義不符合"); //} Trace.Assert(quiz_question != null); Trace.Assert(paper_question != null); Trace.Assert(paper_question.SelectNodes("item").Count == quiz_question.SelectNodes("item").Count); // // 如果都沒作答, 此題放棄 // if (paper_question.SelectNodes("item[@checked='true']").Count == 0) { //Console.WriteLine("偵測到沒作答的答案,此題放棄"); Trace.WriteLine("偵測到沒作答的答案,此題放棄"); return 0; } // // 題目的配分 // int quiz_score = int.Parse(quiz_question.GetAttribute("score")); // // 答對一個選項的分數 // int item_score = quiz_score / itemCount; for (int itemPos = 0; itemPos < itemCount; itemPos++) { XmlElement quiz_item = quiz_question.SelectNodes("item")[itemPos] as XmlElement; XmlElement paper_item = paper_question.SelectNodes("item")[itemPos] as XmlElement; // // 算成積 // if (quiz_item.GetAttribute("correct") == paper_item.GetAttribute("checked")) { totalScore += item_score; } else { totalScore -= item_score; } } Trace.Assert(totalScore >= (0 - quiz_score)); Trace.Assert(totalScore <= quiz_score); return totalScore; }我特地把之前加的亂七八糟的 check code 用註解留下來,各位可以看看用 TRACE / ASSERT 前後的差別有多少。ASSERT是其中的精華。你可以到處都加上 ASSERT ,來說明你對於程式執行到某個地方的 "假設"。舉例來說,你 "假設" 呼叫你 FUNC 的人一定會傳 quizDoc 跟 paperDoc 給你,你又不想為了它寫一堆 IF ....,你就可以簡單的加上這一行 ASSERT( quizDoc != null), 代表只有 quizDoc 不是 NULL 時才是 "正常" 的。
能透過 Http Response Header (Date) 偵測網站的時鐘, 顯示該網站目前的時間的小工具。 通常在搶購演唱會門票,購物網站限時搶購,或是連續假期訂購車票時,都需要在開放購買的那一秒鐘按下按鈕,此時搭配這個工具就能精準地掌握 server 的時間。
示範 API 與 SDK 之間的版本相容性開發範例。示範的開發過程,採用 git flow 的方式來進行版本控制,也包含了透過單元測試,同時採用舊版的 client 測試新版的 server, 確保 API 升級時的相容性問題。
IP to country search service / sdk / auto update worker.
介紹跨網站傳遞授權資訊時的安全問題,用100行以內的程式示範數位簽章如何解決這個問題。也示範了這樣的技巧能解決那些更複雜的授權問題。有文字版本可以閱讀,也提供介紹的錄影,以及相關的 PPT 跟 source code.
2016/12/15, Microservice Training Course, 企業內訓, 分為四次課程, 每次 3hr, 共計 12 hr