重慶分公司,新征程啟航
為企業提供網站建設、域名注冊、服務器等服務
為企業提供網站建設、域名注冊、服務器等服務
通過var聲明或者make函數創建的channel變量是一個存儲在函數棧幀上的指針,占用8個字節,指向堆上的hchan結構體
昭平網站制作公司哪家好,找創新互聯!從網頁設計、網站建設、微信開發、APP開發、成都響應式網站建設等網站項目制作,到程序開發,運營維護。創新互聯自2013年創立以來到現在10年的時間,我們擁有了豐富的建站經驗和運維經驗,來保證我們的工作的順利進行。專注于網站建設就選創新互聯。
源碼包中src/runtime/chan.go定義了hchan的數據結構如下:
hchan結構體的主要組成部分有四個:
用來保存goroutine之間傳遞數據的循環數組:buf
用來記錄此循環數組當前發送或接收數據的下標值:sendx和recvx
用于保存向該chan發送和從該chan接收數據被阻塞的goroutine隊列: sendq 和 recvq
保證channel寫入和讀取數據時線程安全的鎖:lock
環形數組作為channel 的緩沖區 數組的長度就是定義channnel 時channel 的緩沖大小
在hchan 中包括了讀/寫 等待隊列, waitq是一個雙向隊列,包括了一個頭結點和尾節點。 每個節點是一個sudog結構體變量
channel有2種類型:無緩沖、有緩沖, 在創建時 make(chan type cap) 通過cap 設定緩沖大小
channel有3種模式:寫操作模式(單向通道)、讀操作模式(單向通道)、讀寫操作模式(雙向通道)
channel有3種狀態:未初始化、正常、關閉
如下幾種狀態會引發panic
channel 是線程安全的,channel的底層實現中,hchan結構體中采用Mutex鎖來保證數據讀寫安全。在對循環數組buf中的數據進行入隊和出隊操作時,必須先獲取互斥鎖,才能操作channel數據
文件分類:文本文件和二進制文件
文本文件可讀性好,占用的數據空間大
二進制文件,可讀性差,占用的數據空間小
文件存取方式:隨機存取和順序存放
隨機存取:操作速度慢,對磁盤的消耗大
順序存放:操作數據塊,對磁盤的消耗小
初級方法
高級方法
在程序和文件之間,添加一個緩沖區,每次程序讀取文件內容的時候,先去緩沖區查看,如果需要的內容,直接獲取,如果沒有再去文件中獲取
由于緩沖是在內存當中的,和程序的交互返回速度會非常快,這樣可以大大提高程序的性能和速度
缺點:有的數據是只在緩沖中存儲的,如果在緩沖釋放之前,沒有將數據實例化落盤,會導致數據的丟失
按行操作文件對象
將之前的file方法封裝起來,可以更加方便的使用
使用gzip.NewReader(文件句柄),來操作壓縮文件
示例: file,err := os.OpenFile("main.go", os.O_WRONLY|os.O_WRONLY, 0666)
三個參數,
文件操作方法,需要注意不能沖突
操作完成后,當前目錄出現一個text.txt 文件,內容是:hello world,test
這里可以可以考慮使用buffio來實現
Go 的select語句是一種僅能用于channl發送和接收消息的專用語句,此語句運行期間是阻塞的;當select中沒有case語句的時候,會阻塞當前的groutine。所以,有人也會說select是用來阻塞監聽goroutine的。
還有人說:select是Golang在語言層面提供的I/O多路復用的機制,其專門用來檢測多個channel是否準備完畢:可讀或可寫。
以上說法都正確。
我們來回顧一下是什么是 I/O多路復用 。
每來一個進程,都會建立連接,然后阻塞,直到接收到數據返回響應。
普通這種方式的缺點其實很明顯:系統需要創建和維護額外的線程或進程。因為大多數時候,大部分阻塞的線程或進程是處于等待狀態,只有少部分會接收并處理響應,而其余的都在等待。系統為此還需要多做很多額外的線程或者進程的管理工作。
為了解決圖中這些多余的線程或者進程,于是有了"I/O多路復用"
每個線程或者進程都先到圖中”裝置“中注冊,然后阻塞,然后只有一個線程在”運輸“,當注冊的線程或者進程準備好數據后,”裝置“會根據注冊的信息得到相應的數據。從始至終kernel只會使用圖中這個黃黃的線程,無需再對額外的線程或者進程進行管理,提升了效率。
select的實現經歷了多個版本的修改,當前版本為:1.11
select這個語句底層實現實際上主要由兩部分組成: case語句 和 執行函數 。
源碼地址為:/go/src/runtime/select.go
每個case語句,單獨抽象出以下結構體:
結構體可以用下圖表示:
然后執行select語句實際上就是調用 func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) 函數。
func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) 函數參數:
selectgo 返回所選scase的索引(該索引與其各自的select {recv,send,default}調用的序號位置相匹配)。此外,如果選擇的scase是接收操作(recv),則返回是否接收到值。
誰負責調用 func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) 函數呢?
在 /reflect/value.go 中有個 func rselect([]runtimeSelect) (chosen int, recvOK bool) 函數,此函數的實現在 /runtime/select.go 文件中的 func reflect_rselect(cases []runtimeSelect) (int, bool) 函數中:
那誰調用的 func rselect([]runtimeSelect) (chosen int, recvOK bool) 呢?
在 /refect/value.go 中,有一個 func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) 的函數,其調用了 rselect 函數,并將最終Go中select語句的返回值的返回。
以上這三個函數的調用棧按順序如下:
這仨函數中無論是返回值還是參數都大同小異,可以簡單粗暴的認為:函數參數傳入的是case語句,返回值返回被選中的case語句。
那誰調用了 func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) 呢?
可以簡單的認為是系統了。
來個簡單的圖:
前兩個函數 Select 和 rselect 都是做了簡單的初始化參數,調用下一個函數的操作。select真正的核心功能,是在最后一個函數 func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) 中實現的。
打亂傳入的case結構體順序
鎖住其中的所有的channel
遍歷所有的channel,查看其是否可讀或者可寫
如果其中的channel可讀或者可寫,則解鎖所有channel,并返回對應的channel數據
假如沒有channel可讀或者可寫,但是有default語句,則同上:返回default語句對應的scase并解鎖所有的channel。
假如既沒有channel可讀或者可寫,也沒有default語句,則將當前運行的groutine阻塞,并加入到當前所有channel的等待隊列中去。
然后解鎖所有channel,等待被喚醒。
此時如果有個channel可讀或者可寫ready了,則喚醒,并再次加鎖所有channel,
遍歷所有channel找到那個對應的channel和G,喚醒G,并將沒有成功的G從所有channel的等待隊列中移除。
如果對應的scase值不為空,則返回需要的值,并解鎖所有channel
如果對應的scase為空,則循環此過程。
在想想select和channel做了什么事兒,我覺得和多路復用是一回事兒
類型 在變量名后邊
也可不顯式聲明類型, 類型推斷, 但是是靜態語言, name一開始放字符串就不能再賦值數字
方法,屬性 分開 方法名首字母大寫就是就是外部可調的
面向對象設計的一個重要原則:“優先使用組合而不是繼承”
Dog 也是Animal , 要復用Animal 的屬性和方法,
只需要在結構體 type 里面寫 Animal
入口也是main, 用用試試
多態, 有這個方法就是這個接口的實現, 具體的類 不需要知道自己實現了什么接口,
使用: 在一個函數調用之前加上關鍵字go 就啟動了一個goroutine
創建一個goroutine,它會被加入到一個全局的運行隊列當中,
調度器 會把他們分配給某個 邏輯處理器 的隊列,
一個邏輯處理器 綁定到一個 操作系統線程 ,在上面運行goroutine,
如果goroutine需要讀寫文件, 阻塞 ,就脫離邏輯處理器 直接 goroutine - 系統線程 綁定
編譯成同名.exe 來執行, 不通過虛擬機, 直接是機器碼, 和C 一樣, 所以非常快
但是也有自動垃圾回收,每個exe文件當中已經包含了一個類似于虛擬機的runtime,進行goroutine的調度
默認是靜態鏈接的,那個exe會把運行時所需要的所有東西都加進去,這樣就可以把exe復制到任何地方去運行了, 因此 生成的 .exe 文件非常大
import "workname/packetfolder"
導入多個包
方法調用 包名.函數//不是函數或結構體所處文件或文件夾名
packagename.Func()
前面加個點表示省略調用,那么調用該模塊里面的函數,可以不用寫模塊名稱了:
當導入一個包時,該包下的文件里所有init()函數都會被執行,然而,有些時候我們并不需要把整個包都導入進來,僅僅是是希望它執行init()函數而已。下劃線的作用僅僅是為了調用init()函數,所以無法通過包名來調用包中的其他函數
import _ package
變量聲明必須要使用否則會報錯。
全局變量運行聲明但不使用。
func 函數名 (參數1,參數2,...) (返回值a 類型a, 返回值b 類型b,...)
func 函數名 (參數1,參數2,...) (返回值類型1, 返回值類型2,...)
func (this *結構體名) 函數名(參數 string) (返回值類型1, 返回值類型2){}
使用大小來區分函數可見性
大寫是public類型
小寫是private類型
func prifunc int{}
func pubfunc int{}
聲明靜態變量
const value int
定義變量
var value int
聲明一般類型、接口和結構體
聲明函數
func function () int{}
go里面所有的空值對應如下
通道類型
內建函數 new 用來分配內存,它的第一個參數是一個類型,不是一個值,它的返回值是一個指向新分配類型零值的指針
func new(Type) *Type
[這位博主有非常詳細的分析]
Go 語言支持并發,我們只需要通過 go 關鍵字來開啟 goroutine 即可。
goroutine 是輕量級線程,goroutine 的調度是由 Golang 運行時進行管理的。
同一個程序中的所有 goroutine 共享同一個地址空間。
語法格式如下:
通道(channel)是用來傳遞數據的一個數據結構。
通道的聲明
通道可用于兩個 goroutine 之間通過傳遞一個指定類型的值來同步運行和通訊。操作符 - 用于指定通道的方向,發送或接收。如果未指定方向,則為雙向通道。
[這里有比較詳細的用例]
go里面的空接口可以指代任何類型(無論是變量還是函數)
聲明空接口
go里面的的強制類型轉換語法為:
int(data)
如果是接口類型的強制轉成其他類型的語法為:
go里面的強制轉換是將值復制過去,所以在數據量的時候有比較高的運行代價
使用簡單的 make 調用創建的通道叫做無緩沖通道,但 make 還可以接受第二個可選參數,一個表示通道容量的整數。如果容量是 0,make 創建一個無緩沖通道。
無緩沖通道上的發送操作將被阻塞,直到另一個 goroutine 在對應的通道上執行接受操作,這時值傳送完成,兩個 goroutine 都可以繼續執行。相反,如果接受操作先執行,接收方 goroutine 將阻塞,直到另一個 goroutine 在同一個通道上發送一個值。使用無緩沖通道進行的通信導致發送和接受操作 goroutine 同步化。因此,無緩沖通道也稱為同步通道。當一個值在無緩沖通道上傳遞時,接受值后發送方 goroutine 才能被喚醒。
緩沖通道上的發送操作在隊列的尾部插入一個元素,接收操作從隊列的頭部移除一個元素。如果通道滿了,發送操作會阻塞所在的 goroutine 直到另一個 goroutine 對它進行接收操作來留出可用的空間。反過來,如果通道是空的,執行接收操作的 goroutine 阻塞,直到另一個 goroutine 在通道上發送數據。
如果給一個 nil 的 channel 發送數據,會造成永遠阻塞。
如果從一個 nil 的 channel 中接收數據,也會造成永久阻塞。 給一個已經關閉的 channel 發送數據, 會引起 panic。
從一個已經關閉的 channel 接收數據, 如果緩沖區中為空,則返回一個 零 值。