欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

go語言如何擴容 go語言的用途

GoLang中的切片擴容機制

[5]int 是數(shù)組,而 []int 是切片。二者看起來相似,實則是根本上不同的數(shù)據(jù)結(jié)構(gòu)。

站在用戶的角度思考問題,與客戶深入溝通,找到濟寧網(wǎng)站設計與濟寧網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗,讓設計與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個性化、用戶體驗好的作品,建站類型包括:網(wǎng)站設計制作、網(wǎng)站設計、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣、域名注冊、虛擬空間、企業(yè)郵箱。業(yè)務覆蓋濟寧地區(qū)。

切片的數(shù)據(jù)結(jié)構(gòu)中,包含一個指向數(shù)組的指針 array ,當前長度 len ,以及最大容量 cap 。在使用 make([]int, len) 創(chuàng)建切片時,實際上還有第三個可選參數(shù) cap ,也即 make([]int, len, cap) 。在不聲明 cap 的情況下,默認 cap=len 。當切片長度沒有超過容量時,對切片新增數(shù)據(jù),不會改變 array 指針的值。

當對切片進行 append 操作,導致長度超出容量時,就會創(chuàng)建新的數(shù)組,這會導致和原有切片的分離。在下例中

由于 a 的長度超出了容量,所以切片 a 指向了一個增長后的新數(shù)組,而 b 仍然指向原來的老數(shù)組。所以之后對 a 進行的操作,對 b 不會產(chǎn)生影響。

試比較

本例中, a 的容量為6,因此在 append 后并未超出容量,所以 array 指針沒有改變。因此,對 a 進行的操作,對 b 同樣產(chǎn)生了影響。

下面看看用 a := []int{} 這種方式來創(chuàng)建切片會是什么情況。

可以看到,空切片的容量為0,但后面向切片中添加元素時,并不是每次切片的容量都發(fā)生了變化。這是因為,如果增大容量,也即需要創(chuàng)建新數(shù)組,這時還需要將原數(shù)組中的所有元素復制到新數(shù)組中,開銷很大,所以GoLang設計了一套擴容機制,以減少需要創(chuàng)建新數(shù)組的次數(shù)。但這導致無法很直接地判斷 append 時是否創(chuàng)建了新數(shù)組。

如果一次添加多個元素,容量又會怎樣變化呢?試比較下面兩個例子:

那么,是不是說,當向一個空切片中插入 2n-1 個元素時,容量就會被設置為 2n 呢?我們來試試其他的數(shù)據(jù)類型。

可以看到,根據(jù)切片對應數(shù)據(jù)類型的不同,容量增長的方式也有很大的區(qū)別。相關的源碼包括: src/runtime/msize.go , src/runtime/mksizeclasses.go 等。

我們再看看切片初始非空的情形。

可以看到,與剛剛向空切片添加5個int的情況一致,向有3個int的切片中添加2個int,容量增長為6。

需要注意的是, append 對切片擴容時,如果容量超過了一定范圍,處理策略又會有所不同??梢钥纯聪旅孢@個例子。

具體為什么會是這樣的變化過程,還需要從 源碼 中尋找答案。下面是 src/runtime/slice.go 中的 growslice 函數(shù)中的核心部分。

GoLang中的切片擴容機制,與切片的數(shù)據(jù)類型、原本切片的容量、所需要的容量都有關系,比較復雜。對于常見數(shù)據(jù)類型,在元素數(shù)量較少時,大致可以認為擴容是按照翻倍進行的。但具體情況需要具體分析。

go語言string之Buffer與Builder

操作字符串離不開字符串的拼接,但是Go中string是只讀類型,大量字符串的拼接會造成性能問題。

拼接字符串,無外乎四種方式,采用“+”,“fmt.Sprintf()”,"bytes.Buffer","strings.Builder"

上面我們創(chuàng)建10萬字符串拼接的測試,可以發(fā)現(xiàn)"bytes.Buffer","strings.Builder"的性能最好,約是“+”的1000倍級別。

這是由于string是不可修改的,所以在使用“+”進行拼接字符串,每次都會產(chǎn)生申請空間,拼接,復制等操作,數(shù)據(jù)量大的情況下非常消耗資源和性能。而采用Buffer等方式,都是預先計算拼接字符串數(shù)組的總長度(如果可以知道長度),申請空間,底層是slice數(shù)組,可以以append的形式向后進行追加。最后在轉(zhuǎn)換為字符串。這申請了不斷申請空間的操作,也減少了空間的使用和拷貝的次數(shù),自然性能也高不少。

bytes.buffer是一個緩沖byte類型的緩沖器存放著都是byte

是一個變長的 buffer,具有 Read 和Write 方法。 Buffer 的 零值 是一個 空的 buffer,但是可以使用,底層就是一個 []byte, 字節(jié)切片。

向Buffer中寫數(shù)據(jù),可以看出Buffer中有個Grow函數(shù)用于對切片進行擴容。

從Buffer中讀取數(shù)據(jù)

strings.Builder的方法和bytes.Buffer的方法的命名幾乎一致。

但實現(xiàn)并不一致,Builder的Write方法直接將字符拼接slice數(shù)組后。

其沒有提供read方法,但提供了strings.Reader方式

Reader 結(jié)構(gòu):

Buffer:

Builder:

可以看出Buffer和Builder底層都是采用[]byte數(shù)組進行裝載數(shù)據(jù)。

先來說說Buffer:

創(chuàng)建好Buffer是一個empty的,off 用于指向讀寫的尾部。

在寫的時候,先判斷當前寫入字符串長度是否大于Buffer的容量,如果大于就調(diào)用grow進行擴容,擴容申請的長度為當前寫入字符串的長度。如果當前寫入字符串長度小于最小字節(jié)長度64,直接創(chuàng)建64長度的[]byte數(shù)組。如果申請的長度小于二分之一總?cè)萘繙p去當前字符總長度,說明存在很大一部分被使用但已讀,可以將未讀的數(shù)據(jù)滑動到數(shù)組頭。如果容量不足,擴展2*c + n 。

其String()方法就是將字節(jié)數(shù)組強轉(zhuǎn)為string

Builder是如何實現(xiàn)的。

Builder采用append的方式向字節(jié)數(shù)組后添加字符串。

從上面可以看出,[]byte的內(nèi)存大小也是以倍數(shù)進行申請的,初始大小為 0,第一次為大于當前申請的最大 2 的指數(shù),不夠進行翻倍.

可以看出如果舊容量小于1024進行翻倍,否則擴展四分之一。(2048 byte 后,申請策略的調(diào)整)。

其次String()方法與Buffer的string方法也有明顯區(qū)別。Buffer的string是一種強轉(zhuǎn),我們知道在強轉(zhuǎn)的時候是需要進行申請空間,并拷貝的。而Builder只是指針的轉(zhuǎn)換。

這里我們解析一下 *(*string)(unsafe.Pointer(b.buf)) 這個語句的意思。

先來了解下unsafe.Pointer 的用法。

也就是說,unsafe.Pointer 可以轉(zhuǎn)換為任意類型,那么意味著,通過unsafe.Pointer媒介,程序繞過類型系統(tǒng),進行地址轉(zhuǎn)換而不是拷貝。

即*A = Pointer = *B

就像上面例子一樣,將字節(jié)數(shù)組轉(zhuǎn)為unsafe.Pointer類型,再轉(zhuǎn)為string類型,s和b中內(nèi)容一樣,修改b,s也變了,說明b和s是同一個地址。但是對s重新賦值后,意味著s的地址指向了“WORLD”,它們所使用的內(nèi)存空間不同了,所以s改變后,b并不會改變。

所以他們的區(qū)別就在于 bytes.Buffer 是重新申請了一塊空間,存放生成的string變量, 而strings.Builder直接將底層的[]byte轉(zhuǎn)換成了string類型返回了回來,去掉了申請空間的操作。

go語言循環(huán)隊列的實現(xiàn)

隊列的概念在 順序隊列 中,而使用循環(huán)隊列的目的主要是規(guī)避假溢出造成的空間浪費,在使用循環(huán)隊列處理假溢出時,主要有三種解決方案

本文提供后兩種解決方案。

順序隊和循環(huán)隊列是一種特殊的線性表,與順序棧類似,都是使用一組地址連續(xù)的存儲單元依次存放自隊頭到隊尾的數(shù)據(jù)元素,同時附設隊頭(front)和隊尾(rear)兩個指針,但我們要明白一點,這個指針并不是指針變量,而是用來表示數(shù)組當中元素下標的位置。

本文使用切片來完成的循環(huán)隊列,由于一開始使用三個參數(shù)的make關鍵字創(chuàng)建切片,在輸出的結(jié)果中不包含nil值(看起來很舒服),而且在驗證的過程中發(fā)現(xiàn)使用append()函數(shù)時切片內(nèi)置的cap會發(fā)生變化,在消除了種種障礙后得到了一個四不像的循環(huán)隊列,即設置的指針是順序隊列的指針,但實際上進行的操作是順序隊列的操作。最后是對make()函數(shù)和append()函數(shù)的一些使用體驗和小結(jié),隊列的應用放在鏈隊好了。

官方描述(片段)

即切片是一個抽象層,底層是對數(shù)組的引用。

當我們使用

構(gòu)建出來的切片的每個位置的值都被賦為interface類型的初始值nil,但是nil值也是有大小的。

而使用

來進行初始化時,雖然生成的切片中不包含nil值,但是無法通過設置的指針變量來完成入隊和出隊的操作,只能使用append()函數(shù)來進行操作

在go語言中,切片是一片連續(xù)的內(nèi)存空間加上長度與容量的標識,比數(shù)組更為常用。使用 append 關鍵字向切片中追加元素也是常見的切片操作

正是基于此,在使用go語言完成循環(huán)隊列時,首先想到的就是使用make(type, len, cap)關鍵字方式完成切片初始化,然后使用append()函數(shù)來操作該切片,但這一方式出現(xiàn)了很多問題。在使用append()函數(shù)時,切片的cap可能會發(fā)生變化,用不好就會發(fā)生擴容或收縮。最終造成的結(jié)果是一個四不像的結(jié)果,入隊和出隊操作變得與指針變量無關,失去了作為循環(huán)隊列的意義,用在順序隊列還算合適。

參考博客:

Go語言中的Nil

Golang之nil

Go 語言設計與實現(xiàn)

Go語言——goroutine并發(fā)模型

參考:

Goroutine并發(fā)調(diào)度模型深度解析手擼一個協(xié)程池

Golang 的 goroutine 是如何實現(xiàn)的?

Golang - 調(diào)度剖析【第二部分】

OS線程初始棧為2MB。Go語言中,每個goroutine采用動態(tài)擴容方式,初始2KB,按需增長,最大1G。此外GC會收縮??臻g。

BTW,增長擴容都是有代價的,需要copy數(shù)據(jù)到新的stack,所以初始2KB可能有些性能問題。

更多關于stack的內(nèi)容,可以參見大佬的文章。 聊一聊goroutine stack

用戶線程的調(diào)度以及生命周期管理都是用戶層面,Go語言自己實現(xiàn)的,不借助OS系統(tǒng)調(diào)用,減少系統(tǒng)資源消耗。

Go語言采用兩級線程模型,即用戶線程與內(nèi)核線程KSE(kernel scheduling entity)是M:N的。最終goroutine還是會交給OS線程執(zhí)行,但是需要一個中介,提供上下文。這就是G-M-P模型

Go調(diào)度器有兩個不同的運行隊列:

go1.10\src\runtime\runtime2.go

Go調(diào)度器根據(jù)事件進行上下文切換。

調(diào)度的目的就是防止M堵塞,空閑,系統(tǒng)進程切換。

詳見 Golang - 調(diào)度剖析【第二部分】

Linux可以通過epoll實現(xiàn)網(wǎng)絡調(diào)用,統(tǒng)稱網(wǎng)絡輪詢器N(Net Poller)。

文件IO操作

上面都是防止M堵塞,任務竊取是防止M空閑

每個M都有一個特殊的G,g0。用于執(zhí)行調(diào)度,gc,棧管理等任務,所以g0的棧稱為調(diào)度棧。g0的棧不會自動增長,不會被gc,來自os線程的棧。

go1.10\src\runtime\proc.go

G沒辦法自己運行,必須通過M運行

M通過通過調(diào)度,執(zhí)行G

從M掛載P的runq中找到G,執(zhí)行G

Go切片數(shù)組深度解析

Go 中的分片數(shù)組,實際上有點類似于Java中的ArrayList,是一個可以擴展的數(shù)組,但是Go中的切片由比較靈活,它和數(shù)組很像,也是基于數(shù)組,所以在了解Go切片前我們先了解下數(shù)組。

數(shù)組簡單描述就由相同類型元素組成的數(shù)據(jù)結(jié)構(gòu), 在創(chuàng)建初期就確定了長度,是不可變的。

但是Go的數(shù)組類型又和C與Java的數(shù)組類型不一樣, NewArray 用于創(chuàng)建一個數(shù)組,從源碼中可以看出最后返回的是 Array{}的指針,并不是第一個元素的指針,在Go中數(shù)組屬于值類型,在進行傳遞時,采取的是值傳遞,通過拷貝整個數(shù)組。Go語言的數(shù)組是一種有序的struct。

Go 語言的數(shù)組有兩種不同的創(chuàng)建方式,一種是顯示的初始化,一種是隱式的初始化。

注意一定是使用 [...]T 進行創(chuàng)建,使用三個點的隱式創(chuàng)建,編譯器會對數(shù)組的大小進行推導,只是Go提供的一種語法糖。

其次,Go中數(shù)組的類型,是由數(shù)值類型和長度兩個一起確定的。[2]int 和 [3]int 不是同一個類型,不能進行傳參和比較,把數(shù)組理解為類型和長度兩個屬性的結(jié)構(gòu)體,其實就一目了然了。

Go中的數(shù)組屬于值類型,通常應該存儲于棧中,局部變量依然會根據(jù)逃逸分析確定存儲棧還是堆中。

編譯器對數(shù)組函數(shù)中做兩種不同的優(yōu)化:

在靜態(tài)區(qū)完成賦值后復制到棧中。

總結(jié)起來,在不考慮逃逸分析的情況下,如果數(shù)組中元素的個數(shù)小于或者等于 4 個,那么所有的變量會直接在棧上初始化,如果數(shù)組元素大于 4 個,變量就會在靜態(tài)存儲區(qū)初始化然后拷貝到棧上。

由于數(shù)組是值類型,那么賦值和函數(shù)傳參操作都會復制整個數(shù)組數(shù)據(jù)。

不管是賦值或函數(shù)傳參,地址都不一致,發(fā)生了拷貝。如果數(shù)組的數(shù)據(jù)較大,則會消耗掉大量內(nèi)存。那么為了減少拷貝我們可以主動的傳遞指針呀。

地址是一樣的,不過傳指針會有一個弊端,從打印結(jié)果可以看到,指針地址都是同一個,萬一原數(shù)組的指針指向更改了,那么函數(shù)里面的指針指向都會跟著更改。

同樣的我們將數(shù)組轉(zhuǎn)換為切片,通過傳遞切片,地址是不一樣的,數(shù)組值相同。

切片是引用傳遞,所以它們不需要使用額外的內(nèi)存并且比使用數(shù)組更有效率。

所以,切片屬于引用類型。

通過這種方式可以將數(shù)組轉(zhuǎn)換為切片。

中間不加三個點就是切片,使用這種方式創(chuàng)建切片,實際上是先創(chuàng)建數(shù)組,然后再通過第一種方式創(chuàng)建。

使用make創(chuàng)建切片,就不光編譯期了,make創(chuàng)建切片會涉及到運行期。1. 切片的大小和容量是否足夠??;

切片是否發(fā)生了逃逸,最終在堆上初始化。如果切片小的話會先在?;蜢o態(tài)區(qū)進行創(chuàng)建。

切片有一個數(shù)組的指針,len是指切片的長度, cap指的是切片的容量。

cap是在初始化切片是生成的容量。

發(fā)現(xiàn)切片的結(jié)構(gòu)體是數(shù)組的地址指針array unsafe.Pointer,而Go中數(shù)組的地址代表數(shù)組結(jié)構(gòu)體的地址。

slice 中得到一塊內(nèi)存地址,array[0]或者unsafe.Pointer(array[0])。

也可以通過地址構(gòu)造切片

nil切片:指的unsafe.Pointer 為nil

空切片:

創(chuàng)建的指針不為空,len和cap為空

當一個切片的容量滿了,就需要擴容了。怎么擴,策略是什么?

如果原來數(shù)組切片的容量已經(jīng)達到了最大值,再想擴容, Go 默認會先開一片內(nèi)存區(qū)域,把原來的值拷貝過來,然后再執(zhí)行 append() 操作。這種情況對現(xiàn)數(shù)組的地址和原數(shù)組地址不相同。

從上面結(jié)果我們可以看到,如果用 range 的方式去遍歷一個切片,拿到的 Value 其實是切片里面的值拷貝,即淺拷貝。所以每次打印 Value 的地址都不變。

由于 Value 是值拷貝的,并非引用傳遞,所以直接改 Value 是達不到更改原切片值的目的的,需要通過 slice[index] 獲取真實的地址。

文章名稱:go語言如何擴容 go語言的用途
分享網(wǎng)址:http://chinadenli.net/article32/dodejsc.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供云服務器、網(wǎng)站改版、品牌網(wǎng)站建設、外貿(mào)建站、動態(tài)網(wǎng)站、做網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站建設