作為一個ASP.NET開發人員,在之前的開發經歷中接觸多線程編程的機會並不是很多,但是隨著.NET 4.0的發布臨近,我越來越感受到未來的1-2年中並行計算將會有很大的應用。於是決定通過寫日志的方式 來總結一下.NET 3.5下的多線程編程進而引入.NET 4.0提供的新的並行庫以及新的並行編程模式和編程的 思維方式。
個人覺得在日常的編程中對於ASP.NET程序員來說使用多線程編程不是很多,其實我們無時無刻不在享 受多線程的優勢。首先,WEB服務器環境就是一個多線程環境,每一個請求都是獨立的線程,如果沒有多 線程很難想象只能同步處理一個請求的WEB服務器有什麼用,類似,我們的數據庫也應該是一個多線程環 境。對於Windows應用程序的程序員來說恐怕就很難不接觸多線程了,最簡單的就是我們會新開線程去做 一些耗時的操作,這樣就可以避免UI停止響應,在操作結束後再把操作結果應用在主線程的控件上。雖然 說這樣的應用是多線程,甚至很多程序員習慣什麼操作都新開一個線程去做,但是我覺得這樣的多線程應 用的思維還停留在單核時代,在多核時代,我們確實可以讓任務實際的並行執行而不是看上去並行執行。
首先來說說概念,進程和線程的基本的概念不用多說,自然我們也能理解一個進程至少包含一個線程 。通過在一個進程中開啟多個線程,我們就可以讓一個程序在同一時間看上去能做多個事情,比如可以在 接受用戶響應的時候進行一些計算。在以前處理器往往只有一個核心,也就是說在同一時間,處理器只能 做一件事情。那麼怎麼實現之前說的多個線程同時執行呢。其實這個同時只是表面上看上去同時,本質上 多個線程依次占用處理器的若干時間片,大家輪流使用其資源,由於這個時間片非常短,所以在一個長的 時間看來似乎是幾個線程同時得到了執行。
舉一個生動的例子,我們經常看到有一些畫家能同時在一個畫布上畫兩個不同的圖片,一個畫人一個 畫房子,最後一起完成這個畫。但仔細看的話發現,他是兩手拿了兩只畫筆,在這裡畫一筆那裡畫一筆, 在同一時間其實也只有一只筆在畫。這個畫家應該也像普通人一樣是單核的,只是線程切換比較快罷了。 我經常在打電話的時候和網友進行聊天,在同一時間做兩件事情,但是這樣很費腦子,在打字前我要回憶 一下剛才聊天的內容,然後輸入聊天的文字,然後再去回想一下剛才那哥們說了啥,在電話裡面回他一句 ,這種回憶的工作就是准備線程的上下文,交給腦子去處理。雖然同一時間是做了兩件事情,但是這個上 下文的准備工作也浪費了點時間,如果我在打電話網絡聊天的同事在去做第三件事情比如看電影,那我估 計就不行了。所以,線程也不能開的很多,特別對於人腦來說。但是對於電腦處理器來說就不一樣了,你 只要准備好數據和指令他執行就是了,至於這些事情來自幾件事情它不關心,24小時一秒都不浪費在執行 指令完全沒問題,當然你也可以讓它閒著。
您可能會想了,既然線程切換需要時間,那麼我們開兩個線程執行兩個任務不是還沒有一個一個執行 來的快嗎?其實即使對於單核的處理器都不一定,因為在實際的應用中我們的任務往往不可能從頭計算到 尾一直占用處理器資源,在很多時候我們要等待IO響應或用戶的響應,如果只是一個線程做事情的話處理 器太閒了。對於現在多核的處理器來說,在同一時刻理論上可以在每一個處理器上都並行執行指令,我們 就更需要利用多線程來提高運算速度了。當然也不是說一個任務要執行10秒,我們在雙核的機器上並行執 行這個任務只需要5秒了,那是因為很多時候這個任務很難劃分成兩個分支來並行執行,如果每個指令都 要依靠上個指令的執行結果,那麼這樣的操作很難在多個處理器上並行執行。但是,我們可以這樣想,至 少如果有兩個這樣任務的話,我們就可以完全利用多個處理器的優勢來並行執行了。
但是也不是多可以隨便的開線程,每一個線程默認情況下都會占用1M的棧空間(對於普通應用程序來 說),在32位Windows平台下可以給一個用戶進程使用的程序最大在2G,那麼也就是說在程序中使用的線 程不能超過2000個,在實際測試中可以發現一般來說開1930左右個線程就會收到內存不足的異常,其實這 個數量是絕對夠用的,即使復雜的Outlook2007程序一般也只用了50個不到的線程(可以在任務管理器中 觀察到)。
在不得已的情況下很多人都不太會去使用多線程也是有原因的,一是因為多線程編程復雜,我們也習 慣了一行一行代碼執行的編程模式,對於一個好的多線程程序來說,要盡量分割任務讓它在多個線程中使 用以利用到多個處理器核。還有就是多個線程使用相同資源的話還要考慮資源的鎖定以免產生數據的不一 致。鎖定/ 事務/並發的概念在數據庫中也是非常常見的。二是因為調試困難,特別是一個線程的執行依 賴其它線程的執行。三是因為多線程的程序隨著環境的變化(處理器 /操作系統)可能執行的性能還不一 定相同,如果只針對某個環境進行編程可能還不能充分利用多處理器的優勢。比如我們對一個任務劃分成 2個線程並行執行,那麼對於四核的處理器來說,劃分成4個線程並行執行會不會更合理呢,說實話我也舉 的這事挺難說的?還有,我們的編程基於.NET框架,而其本質還是使用的是操作系統的線程,操作系統中 本來就有很多進程運行著,處理器是大家的處理器,不是專供我們程序使用的,在這麼一個魚龍混雜的環 境,我們的程序究竟是不是會表現的如我們預期那樣,也很難說。
多線程好,多線程難,本系列文章也只能在一個比較淺顯的層次來談談如何在.NET框架中進行多線程 編程,以及一些常見應用(比如Windows應用)中多線程的典型應用。本系列文章預計會有30篇這樣的規 模,希望對大家有幫助。