作為併發程式設計乙個基礎硬體知識儲備,首先要說的就是記憶體了,總的來說在絕大多數情況下把記憶體的併發增刪改查模型搞清楚了其他的基本上也是異曲同工之妙。
記憶體晶元——即我們所知道的記憶體顆粒,是一堆mos管的集合,在半導體稱呼裡面,很多mos管組成乙個半導體(組module),很多個module組成乙個管芯(die),這個die即是記憶體顆粒,當然,更上一級即很多die組成的東西叫做晶圓(wafer)。
簡單來說,每8個mos管組成的電路可以表示乙個位元組,比如ascii的『a』,我們使用65表示,即0100 0001,那麼8個mos分別使用低-高-低-低-低-低-低-高電位即可表示字元a。
在對記憶體的寫入和讀取時,通常也是按照8個字開始作為一組進行操作,我們現在常用的cpu是64位,可以一次性處理64/8=8個位元組的資料。
首先明確乙個概念:匯流排是線但是也不是線,以下是來自百科的解釋:
匯流排(bus)是計算機各種功能部件之間傳送資訊的公共通訊幹線,它是由導線組成的傳輸線束, 按照計算機所傳輸的資訊種類,計算機的匯流排可以劃分為資料匯流排、位址匯流排和控制匯流排,分別用來傳輸資料、資料位址和控制訊號。匯流排是一種內部結構,它是cpu、記憶體、輸入、輸出裝置傳遞資訊的公用通道。
乙個cpu要操作記憶體的資料,是通過匯流排來進行操作的,通常來說記憶體的讀寫操作不是乙個cpu指令週期能完成的,如果多個程式在同時操作乙個記憶體位址,則有各種意外的讀寫操作。
在單核cpu時期,硬體一次只能處理乙個事情,在多工的情況下不同的任務按需搶占cpu來執行它的**,這裡面就涉及到cpu排程工作,通常情況下,作業系統已經幫我們做了很多事,如果乙個程式語言開啟的併發操作是交給了作業系統的,那麼排程這塊不需要太關心,如果像go這樣有自己的協程排程器,還是需要專門了解下特有的排程方式的。
多核時期,基本原理也差不多,在對於硬體的理解上也可以完全參考單核。
cpu通過位址匯流排去尋找記憶體位址,比如0x00004567這種,64位cpu最大能操作的位址長度為264,32位作業系統則是232長度,所以為什麼32位cpu最大只支援4gb記憶體呢?
package main
import (
"fmt"
)var a int
func main()
fmt.println(a)
}
package main
import (
"fmt"
"time"
)var a int
func main()
time.sleep(1*time.second)
fmt.println(a)
}func add()
我們再看看示例三和示例四:
package main
import (
"fmt"
)func main()
}
package main
import (
"fmt"
"time"
)func main()
time.sleep(1*time.second)
}func add(j int)
示例三其實沒太多好說的,單協程模型,輸出也不會有什麼意外,而示例四大家猜猜是按照1,2,3...9999這樣的順序呢還是其他輸出順序呢?
綜上結果,我們會發現多協程模型裡面的東西沒有順序性,對變數的操作也沒有原子性。
示例五給出了golang中最簡單的加鎖處理方式:
package main
import (
"fmt"
"time"
"sync"
)var a int
var lock *sync.mutex
func main()
time.sleep(1*time.second)
fmt.println(a)
}func add()
而關於多協程順序性方面的實現方式,也可以比著葫蘆畫瓢寫出來,這裡就不再贅述了。
假設在左邊有三堆散亂的磚,我們需要將其從左邊搬運到右邊並堆放整齊,這樣的乙個工作我們從併發模型來看有哪些比較可執行的實現方式呢:
每堆磚頭分配固定的人數,堆磚時為保證堆疊整齊度,採用排隊的方式乙個乙個按先後順序堆疊
拿乙個人專職在左邊遞磚,若干人從左邊的遞磚人處拿磚,搬磚後在右邊排隊堆疊
左邊專人遞磚,右邊專人堆磚,若干搬磚人只負責搬磚
這也是併發程式設計模型中比較常用的程式設計思路,在以後遇到類似問題的時候可以想想這個例子。
我們以乙個實際的案例作為結束,這個案例是匯出某雲平台所屬裝置資訊的**,裡面包含有多協程拉取資料的例項,整體的流程如下:
引數初始化
定義乙個接收協程結束的資訊通道
開啟n個協程
協程呼叫api獲取資訊,按分頁引數每個協程獲取(總數/n)資訊,每次page=x+n
每次獲取的資訊放入excel緩衝區
當最後的分頁獲取不到資訊時向通道寫入東西表示該協程任務完成
主程序迴圈獲取每個協程結束的資訊,直到所有協程任務完成
將excel緩衝區資料寫入excel檔案
結束連線如下:
當然,這個案例在併發上其實還存在較大的提公升空間,聰明的大家看看結合搬磚的例子來怎麼提公升呢。
Golang 併發程式設計
目錄傳送者 通道 有可能有資料阻塞 接受者 package main import fmt time func main time.sleep 2 time.second 主協程取資料 for i 0 i 3 i fmt.println 主協程結束 傳送者 通道 資料 資料 接受者 func mai...
Golang 併發程式設計
目錄傳送者 通道 有可能有資料阻塞 接受者 package main import fmt time func main time.sleep 2 time.second 主協程取資料 for i 0 i 3 i fmt.println 主協程結束 傳送者 通道 資料 資料 接受者 func mai...
golang併發程式設計
在早期,cpu都是以單核的形式順序執行機器指令。c語言 php正是這種順序程式語言的代表,即所有的指令都是以序列的方式執行,在相同的時刻有且僅有乙個cpu在順序執行程式的指令。隨著處理器技術的發展,單核時代以提公升處理器頻率來提高執行效率的方式遇到了瓶頸。單核cpu的發展的停滯,給多核cpu的發展帶...