多線程
多線程是程序員麵試時常常會麵對的問題,對多線程概念的掌握和理解水平,也會被一些老鳥用來衡量一個人的編程實力的重要參考指標。不論是實際工作需要還是為了應付麵試,掌握多線程都是程序員職業生涯中一個必須經過的環節。其實當你把“多線程”和你的“職業生涯”聯係在一起考慮的時候,就會覺得“多線程”是多麼的渺小,對,沒有跨越不過的山。不過就算它很渺小,但也有可能改變你的人生軌跡。不用擔心,如果你對多線程還不太熟悉,那麼我們就一起來看看什麼是多線程吧。
跟前幾篇的風格一樣,我會在開篇的時候舉一個現實生活中的例子,通過這個例子來映射一些晦澀枯燥的計算機編程專業知識,在讓讀者朋友很好地理解理論概念的同時,又避免了閱讀教科書時的枯燥感覺。這次我要舉的例子是公司。不一定是IT公司,盡量和編程領域遠一點兒吧,那就假設是一家搬家公司吧。
假如我們把公司看做是一個進程,那麼人就是其中的線程。進程必須得有一個主線程,公司在創業初期往往可能出現一人打天下的現象,但是,至少得有一個人,公司才能運作。公司創業初期,業務還不算太多,往往就是老板一個人身兼數職,一天如果隻有1、2趟活兒,應該還是忙得過來的。時間長了,隨著業務的發展、口碑的建立,生意越來越興隆,一個人肯定就忙不過來了。假設一天有5個活兒,老板一個人必須搬完A家才能搬B家,搬到黃昏估計也就搬到C家,D和E家都還在焦急地等待著呢。老板一個人要充當搬運工、司機、業務聯係人、法人代表、出納等眾多角色,累死累活公司的規模也上不去,人手不夠製約了公司的發展。那麼怎麼辦,很簡單,增加人手,用編程的話來說就是“再起個線程”。
我們現在就用代碼來描述這樣的場景吧,首先,我們準備成立一家搬家公司,於是要準備好將來和客戶簽的合同書:
1: public class Contract
2: {
3: public string ID { get; private set; }
4: public string From { get; set; }
5: public string To { get; set; }
6: public decimal Fee { get; set; }
7:
8: public Contract()
9: {
10: this.ID = Guid.NewGuid().ToString()
11: }
12: }
簡是簡單了點兒,好歹也是份合同,現在我們就去申請注冊一家公司,並組建好初創團隊,哪怕目前還隻有老板一個人:
1: public class HouseMovingCompany
2: {
3: private static HouseMovingCompany _instance = null;
4: public static HouseMovingCompany Instance
5: {
6: get { return (_instance == null ? _instance = new HouseMovingCompany() : _instance); }
7: }
8:
9: public List<Contract> Contracts { get; private set; }
10:
11: public HouseMovingCompany()
12: {
13: this.Contracts = new List<Contract>();
14: }
15:
16: public void MoveHouse()
17: {
18: if (this.Contracts == null || this.Contracts.Count == 0)
19: {
20: return;
21: }
22:
23: Contract contract = contract = this.Contracts[0];
24: this.Contracts.RemoveAt(0);
25:
26: if (!String.IsNullOrEmpty(contract.From) && !String.IsNullOrEmpty(contract.To))
27: {
28: Console.WriteLine("Move the house from {0} to {1}.", contract.From, contract.To);
29: }
30:
31: Thread.Sleep(5000);
32: }
33: }
好了,現在公司實體有了,老板就可以開始忙活了:
1: static void Main(string[] args)
2: {
3: HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "WuDaokou", To = "LinDa Road", Fee = 500 });
4:
5: while (HouseMovingCompany.Instance.Contracts.Count > 0)
6: {
7: HouseMovingCompany.Instance.MoveHouse();
8: }
9: }
我們在前麵設置了每次搬家耗時5秒鍾,咱們把它想象成5個小時。嗯,一天接一個單子,還可以接受,但是隨著老板生意日漸興隆,有時候一天要接3個單子,這就最少要工作15個小時了,還要操心公司的運營等問題,的確忙不過來了,而且照這樣算,老板一天不可能完成5個或5個以上的單子,嚴重製約了公司的發展:
1: static void Main(string[] args)
2: {
3: HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "WuDaokou", To = "LinDa Road", Fee = 500 });
4: HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "XiDan", To = "WangFujing", Fee = 1000 });
5: HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "XiangShan", To = "The Forbidden City", Fee = 10000 });
6:
7: while (HouseMovingCompany.Instance.Contracts.Count > 0)
8: {
9: HouseMovingCompany.Instance.MoveHouse();
10: }
11: }
一天夜裏,老板拖著疲倦的身子回到家裏,一進門就一頭倒在床上,他極力睜著快睜不開的眼睛,努力地對自己說:“不行,我一定要想個辦法,不然我會被累死的!”。
其實辦法很簡單,誰都知道,招聘幾個員工,再買幾輛車,大家分頭行動,不僅分擔了工作負擔,而且在規模擴大的同時還可以完成更多更大的單子。好,我們現在就借助多線程機製來實現我們的想法:
1: static void Main(string[] args)
2: {
3: HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "WuDaokou", To = "LinDa Road", Fee = 500 });
4: HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "XiDan", To = "WangFujing", Fee = 1000 });
5: HouseMovingCompany.Instance.Contracts.Add(new Contract { From = "XiangShan", To = "The Forbidden City", Fee = 10000 });
6:
7: Thread thread = null;
8:
9: while (HouseMovingCompany.Instance.Contracts.Count > 0)
10: {
11: thread = new Thread(new ThreadStart(HouseMovingCompany.Instance.MoveHouse));
12:
13: thread.Start();
14: }
15: }
在這段程序中,我們分頭行動,讓每項搬家任務都由一個小團隊去完成,結果我們發現,現在做三個單子的時間跟做一個單子的時間是一樣的,提高了效率也擴大了公司規模。但是,既然引入了新的工作機製,我們在公司內部也不得不做一些小小的調整:
1: public void MoveHouse()
2: {
3: if (this.Contracts == null || this.Contracts.Count == 0)
4: {
5: return;
6: }
7:
8: Contract contract = null;
9:
10: lock (this.Contracts)
11: {
12: contract = this.Contracts[0];
13: this.Contracts.RemoveAt(0);
14: }
15:
16: if (!String.IsNullOrEmpty(contract.From) && !String.IsNullOrEmpty(contract.To))
17: {
18: Console.WriteLine("Move the house from {0} to {1}.", contract.From, contract.To);
19: }
20:
21: Thread.Sleep(5000);
22: }
調整的隻是MoveHouse這個方法內部的一些實現細節。公司接到的單子都保存在Contracts中,所以搬家的時候需要去拿一個單子然後根據單子上的信息來工作。原先我們隻有一個線程在操作Contracts,倒也不覺得什麼,現在有多個線程都在對Contracts中的元素進行存取,我們不得不提防一些意外發生。這就是在使用多線程的時候常常需要考慮的並發問題,所以我們用了lock關鍵字,當一個線程要操作Contracts時,它先把Contracts鎖起來,其實就是聲明一下:“現在我在操作它,你們誰都不要動,等我弄完了再說。”在lock塊結束時被鎖定的對象才會被解鎖,其它的線程現在才可以去操作它。
有了多線程機製,你會發現程序可以在更短的時間內完成更多的事情。本文沒有將多線程機製中的所有概念麵麵俱到地列舉出來,但是已經向你展示了該如何使用多線程以及什麼時候可以考慮使用多線程,其它的一些細節有待你去進一步探索,例如,你可以設置線程的優先級(假設邏輯上跟Fee掛鉤,類似於‘加急’)等等。
掌握多線程機製,並讓它使你的應用程序變得更加強悍吧。
最後更新:2017-04-03 21:13:14