目錄程序員
Go的調度器使用了一個叫作GOMAXPROCS的變量來決定會有多少個操做系統的線程同時執行Go的代碼。其默認的值是運行機器上的CPU的核心數,因此在一個有8個核心的機器上時,調度器一次會在8個OS線程上去調度GO代碼。(GOMAXPROCS是前面說的m:n調度中的n)。在休眠中的或者在通訊中被阻塞的goroutine是不須要一個對應的線程來作調度的。在I/O中或系統調用中或調用非Go語言函數時,是須要一個對應的操做系統線程的,可是GOMAXPROCS並不須要將這幾種狀況計算在內。web
你能夠用GOMAXPROCS的環境變量來顯式地控制這個參數,或者也能夠在運行時用runtime.GOMAXPROCS函數來修改它。咱們在下面的小程序中會看到GOMAXPROCS的效果,這個程序會無限打印0和1。shell
for { go fmt.Print(0) fmt.Print(1) }
$ GOMAXPROCS=1 go run hacker-cliché.go 111111111111111111110000000000000000000011111... $ GOMAXPROCS=2 go run hacker-cliché.go 010101010101010101011001100101011010010100110...
在第一次執行時,最多同時只能有一個goroutine被執行。初始狀況下只有main goroutine被執行,因此會打印不少1。過了一段時間後,GO調度器會將其置爲休眠,並喚醒另外一個goroutine,這時候就開始打印不少0了,在打印的時候,goroutine是被調度到操做系統線程上的。在第二次執行時,咱們使用了兩個操做系統線程,因此兩個goroutine能夠一塊兒被執行,以一樣的頻率交替打印0和1。咱們必須強調的是goroutine的調度是受不少因子影響的,而runtime也是在不斷地發展演進的,因此這裏的你實際獲得的結果可能會由於版本的不一樣而與咱們運行的結果有所不一樣。編程
在大多數支持多線程的操做系統和程序語言中,當前的線程都有一個獨特的身份(id),而且這個身份信息能夠以一個普通值的形式被被很容易地獲取到,典型的能夠是一個integer或者指針值。這種狀況下咱們作一個抽象化的thread-local storage(線程本地存儲,多線程編程中不但願其它線程訪問的內容)就很容易,只須要以線程的id做爲key的一個map就能夠解決問題,每個線程以其id就能從中獲取到值,且和其它線程互不衝突。小程序
goroutine沒有能夠被程序員獲取到的身份(id)的概念。這一點是設計上故意而爲之,因爲thread-local storage老是會被濫用。好比說,一個web server是用一種支持tls的語言實現的,而很是廣泛的是不少函數會去尋找HTTP請求的信息,這表明它們就是去其存儲層(這個存儲層有多是tls)查找的。這就像是那些過度依賴全局變量的程序同樣,會致使一種非健康的「距離外行爲」,在這種行爲下,一個函數的行爲可能不是由其本身內部的變量所決定,而是由其所運行在的線程所決定。所以,若是線程自己的身份會改變——好比一些worker線程之類的——那麼函數的行爲就會變得神祕莫測。數據結構
Go鼓勵更爲簡單的模式,這種模式下參數對函數的影響都是顯式的。這樣不只使程序變得更易讀,並且會讓咱們自由地向一些給定的函數分配子任務時不用擔憂其身份信息影響行爲。多線程
你如今應該已經明白了寫一個Go程序所須要的全部語言特性信息。在後面兩章節中,咱們會回顧一些以前的實例和工具,支持咱們寫出更大規模的程序:如何將一個工程組織成一系列的包,如何獲取,構建,測試,性能測試,剖析,寫文檔,而且將這些包分享出去。函數