<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 14.1.1 什么是協程 一個應用程序是運行在機器上的一個進程;進程是一個運行在自己內存地址空間里的獨立執行體。一個進程由一個或多個操作系統線程組成,這些線程其實是共享同一個內存地址空間的一起工作的執行體。幾乎所有'正式'的程序都是多線程的,以便讓用戶或計算機不必等待,或者能夠同時服務多個請求(如 Web 服務器),或增加性能和吞吐量(例如,通過對不同的數據集并行執行代碼)。一個并發程序可以在一個處理器或者內核上使用多個線程來執行任務,但是只有同一個程序在某個時間點同時運行在多核或者多處理器上才是真正的并行。 并行是一種通過使用多處理器以提高速度的能力。所以并發程序可以是并行的,也可以不是。 公認的,使用多線程的應用難以做到準確,最主要的問題是內存中的數據共享,它們會被多線程以無法預知的方式進行操作,導致一些無法重現或者隨機的結果(稱作?`競態`)。 不要使用全局變量或者共享內存,它們會給你的代碼在并發運算的時候帶來危險。 解決之道在于同步不同的線程,對數據加鎖,這樣同時就只有一個線程可以變更數據。在 Go 的標準庫?`sync`?中有一些工具用來在低級別的代碼中實現加鎖;我們在第?[9.3](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/9.3.md)?節中討論過這個問題。不過過去的軟件開發經驗告訴我們這會帶來更高的復雜度,更容易使代碼出錯以及更低的性能,所以這個經典的方法明顯不再適合現代多核/多處理器編程:`thread-per-connection`?模型不夠有效。 Go 更傾向于其他的方式,在諸多比較合適的范式中,有個被稱作?`Communicating Sequential Processes(順序通信處理)`(CSP, C. Hoare 發明的)還有一個叫做?`message passing-model(消息傳遞)`(已經運用在了其他語言中,比如 Erlang)。 在 Go 中,應用程序并發處理的部分被稱作?`goroutines(協程)`,它可以進行更有效的并發運算。在協程和操作系統線程之間并無一對一的關系:協程是根據一個或多個線程的可用性,映射(多路復用,執行于)在他們之上的;協程調度器在 Go 運行時很好的完成了這個工作。 協程工作在相同的地址空間中,所以共享內存的方式一定是同步的;這個可以使用?`sync`?包來實現(參見第?[9.3](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/9.3.md)?節),不過我們很不鼓勵這樣做:Go 使用?`channels`?來同步協程(可以參見第?[14.2](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/14.2.md)?節等章節) 當系統調用(比如等待 I/O)阻塞協程時,其他協程會繼續在其他線程上工作。協程的設計隱藏了許多線程創建和管理方面的復雜工作。 協程是輕量的,比線程更輕。它們痕跡非常不明顯(使用少量的內存和資源):使用 4K 的棧內存就可以在堆中創建它們。因為創建非常廉價,必要的時候可以輕松創建并運行大量的協程(在同一個地址空間中 100,000 個連續的協程)。并且它們對棧進行了分割,從而動態的增加(或縮減)內存的使用;棧的管理是自動的,但不是由垃圾回收器管理的,而是在協程退出后自動釋放。 協程可以運行在多個操作系統線程之間,也可以運行在線程之內,讓你可以很小的內存占用就可以處理大量的任務。由于操作系統線程上的協程時間片,你可以使用少量的操作系統線程就能擁有任意多個提供服務的協程,而且 Go 運行時可以聰明的意識到哪些協程被阻塞了,暫時擱置它們并處理其他協程。 存在兩種并發方式:確定性的(明確定義排序)和非確定性的(加鎖/互斥從而未定義排序)。Go 的協程和通道理所當然的支持確定性的并發方式(例如通道具有一個 sender 和一個 receiver)。我們會在第?[14.7](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/14.7.md)?節中使用一個常見的算法問題(工人問題)來對比兩種處理方式。 協程是通過使用關鍵字?`go`?調用(執行)一個函數或者方法來實現的(也可以是匿名或者 lambda 函數)。這樣會在當前的計算過程中開始一個同時進行的函數,在相同的地址空間中并且分配了獨立的棧,比如:`go sum(bigArray)`,在后臺計算總和。 協程的棧會根據需要進行伸縮,不出現棧溢出;開發者不需要關心棧的大小。當協程結束的時候,它會靜默退出:用來啟動這個協程的函數不會得到任何的返回值。 任何 Go 程序都必須有的?`main()`?函數也可以看做是一個協程,盡管它并沒有通過?`go`?來啟動。協程可以在程序初始化的過程中運行(在?`init()`?函數中)。 在一個協程中,比如它需要進行非常密集的運算,你可以在運算循環中周期的使用?`runtime.Gosched()`:這會讓出處理器,允許運行其他協程;它并不會使當前協程掛起,所以它會自動恢復執行。使用?`Gosched()`?可以使計算均勻分布,使通信不至于遲遲得不到響應。 ## [](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/14.1.md#1412-并發和并行的差異)14.1.2 并發和并行的差異 Go 的并發原語提供了良好的并發設計基礎:表達程序結構以便表示獨立地執行的動作;所以Go的的重點不在于并行的首要位置:并發程序可能是并行的,也可能不是。并行是一種通過使用多處理器以提高速度的能力。但往往是,一個設計良好的并發程序在并行方面的表現也非常出色。 在當前的運行時(2012 年一月)實現中,Go 默認沒有并行指令,只有一個獨立的核心或處理器被專門用于 Go 程序,不論它啟動了多少個協程;所以這些協程是并發運行的,但他們不是并行運行的:同一時間只有一個協程會處在運行狀態。 這個情況在以后可能會發生改變,不過屆時,為了使你的程序可以使用多個核心運行,這時協程就真正的是并行運行了,你必須使用?`GOMAXPROCS`?變量。 這會告訴運行時有多少個協程同時執行。 并且只有 gc 編譯器真正實現了協程,適當的把協程映射到操作系統線程。使用?`gccgo`?編譯器,會為每一個協程創建操作系統線程。 ## [](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/14.1.md#1413-使用-gomaxprocs)14.1.3 使用 GOMAXPROCS 在 gc 編譯器下(6g 或者 8g)你必須設置 GOMAXPROCS 為一個大于默認值 1 的數值來允許運行時支持使用多于 1 個的操作系統線程,所有的協程都會共享同一個線程除非將 GOMAXPROCS 設置為一個大于 1 的數。當 GOMAXPROCS 大于 1 時,會有一個線程池管理許多的線程。通過?`gccgo`?編譯器 GOMAXPROCS 有效的與運行中的協程數量相等。假設 n 是機器上處理器或者核心的數量。如果你設置環境變量 GOMAXPROCS>=n,或者執行?`runtime.GOMAXPROCS(n)`,接下來協程會被分割(分散)到 n 個處理器上。更多的處理器并不意味著性能的線性提升。有這樣一個經驗法則,對于 n 個核心的情況設置 GOMAXPROCS 為 n-1 以獲得最佳性能,也同樣需要遵守這條規則:協程的數量 > 1 + GOMAXPROCS > 1。 所以如果在某一時間只有一個協程在執行,不要設置 GOMAXPROCS! 還有一些通過實驗觀察到的現象:在一臺 1 顆 CPU 的筆記本電腦上,增加 GOMAXPROCS 到 9 會帶來性能提升。在一臺 32 核的機器上,設置 GOMAXPROCS=8 會達到最好的性能,在測試環境中,更高的數值無法提升性能。如果設置一個很大的 GOMAXPROCS 只會帶來輕微的性能下降;設置 GOMAXPROCS=100,使用?`top`?命令和?`H`?選項查看到只有 7 個活動的線程。 增加 GOMAXPROCS 的數值對程序進行并發計算是有好處的; 請看?[goroutine_select2.go](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/examples/chapter_14/goroutine_select2.go) 總結:GOMAXPROCS 等同于(并發的)線程數量,在一臺核心數多于1個的機器上,會盡可能有等同于核心數的線程在并行運行。 ## [](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/14.1.md#1414-如何用命令行指定使用的核心數量)14.1.4 如何用命令行指定使用的核心數量 使用?`flags`?包,如下: ~~~ var numCores = flag.Int("n", 2, "number of CPU cores to use") in main() flag.Pars() runtime.GOMAXPROCS(*numCores) ~~~ 協程可以通過調用`runtime.Goexit()`來停止,盡管這樣做幾乎沒有必要。 示例 14.1-[goroutine1.go](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/examples/chapter_14/goroutine1.go)?介紹了概念: ~~~ package main import ( "fmt" "time" ) func main() { fmt.Println("In main()") go longWait() go shortWait() fmt.Println("About to sleep in main()") // sleep works with a Duration in nanoseconds (ns) ! time.Sleep(10 * 1e9) fmt.Println("At the end of main()") } func longWait() { fmt.Println("Beginning longWait()") time.Sleep(5 * 1e9) // sleep for 5 seconds fmt.Println("End of longWait()") } func shortWait() { fmt.Println("Beginning shortWait()") time.Sleep(2 * 1e9) // sleep for 2 seconds fmt.Println("End of shortWait()") } ~~~ 輸出: ~~~ In main() About to sleep in main() Beginning longWait() Beginning shortWait() End of shortWait() End of longWait() At the end of main() // after 10s ~~~ `main()`,`longWait()`?和?`shortWait()`?三個函數作為獨立的處理單元按順序啟動,然后開始并行運行。每一個函數都在運行的開始和結束階段輸出了消息。為了模擬他們運算的時間消耗,我們使用了?`time`?包中的?`Sleep`?函數。`Sleep()`?可以按照指定的時間來暫停函數或協程的執行,這里使用了納秒(ns,符號 1e9 表示 1 乘 10 的 9 次方,e=指數)。 他們按照我們期望的順序打印出了消息,幾乎都一樣,可是我們明白這是模擬出來的,以并行的方式。我們讓?`main()`?函數暫停 10 秒從而確定它會在另外兩個協程之后結束。如果不這樣(如果我們讓?`main()`?函數停止 4 秒),`main()`?會提前結束,`longWait()`?則無法完成。如果我們不在?`main()`?中等待,協程會隨著程序的結束而消亡。 當?`main()`?函數返回的時候,程序退出:它不會等待任何其他非 main 協程的結束。這就是為什么在服務器程序中,每一個請求都會啟動一個協程來處理,`server()`?函數必須保持運行狀態。通常使用一個無限循環來達到這樣的目的。 另外,協程是獨立的處理單元,一旦陸續啟動一些協程,你無法確定他們是什么時候真正開始執行的。你的代碼邏輯必須獨立于協程調用的順序。 為了對比使用一個線程,連續調用的情況,移除 go 關鍵字,重新運行程序。 現在輸出: ~~~ In main() Beginning longWait() End of longWait() Beginning shortWait() End of shortWait() About to sleep in main() At the end of main() // after 17 s ~~~ 協程更有用的一個例子應該是在一個非常長的數組中查找一個元素。 將數組分割為若干個不重復的切片,然后給每一個切片啟動一個協程進行查找計算。這樣許多并行的協程可以用來進行查找任務,整體的查找時間會縮短(除以協程的數量)。 ## [](https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/14.1.md#1415-go-協程goroutines和協程coroutines)14.1.5 Go 協程(goroutines)和協程(coroutines) (譯者注:標題中的“Go協程(goroutines)” 即是 14 章講的協程指的是 Go 語言中的協程。而“協程(coroutines)”指的是其他語言中的協程概念,僅在本節出現。) 在其他語言中,比如 C#,Lua 或者 Python 都有協程的概念。這個名字表明它和 Go協程有些相似,不過有兩點不同: * Go 協程意味著并行(或者可以以并行的方式部署),協程一般來說不是這樣的 * Go 協程通過通道來通信;協程通過讓出和恢復操作來通信 Go 協程比協程更強大,也很容易從協程的邏輯復用到 Go 協程。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看