真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

go語(yǔ)言可見(jiàn)性 go語(yǔ)言可視化編程

沒(méi)有類(lèi),C語(yǔ)言有結(jié)構(gòu)體,那么Go的結(jié)構(gòu)體有什么特別之處?

Go語(yǔ)言中沒(méi)有“類(lèi)”的概念,也不支持“類(lèi)”的繼承等面向?qū)ο蟮母拍?。Go語(yǔ)言中通過(guò)結(jié)構(gòu)體的內(nèi)嵌再配合接口比面向?qū)ο缶哂懈叩臄U(kuò)展性和靈活性。

創(chuàng)新互聯(lián)公司專(zhuān)注于平潭企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè),商城建設(shè)。平潭網(wǎng)站建設(shè)公司,為平潭等地區(qū)提供建站服務(wù)。全流程按需制作網(wǎng)站,專(zhuān)業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)公司專(zhuān)業(yè)和態(tài)度為您提供的服務(wù)

自定義類(lèi)型

在Go語(yǔ)言中有一些基本的數(shù)據(jù)類(lèi)型,如string、整型、浮點(diǎn)型、布爾等數(shù)據(jù)類(lèi)型, Go語(yǔ)言中可以使用type關(guān)鍵字來(lái)定義自定義類(lèi)型。

自定義類(lèi)型是定義了一個(gè)全新的類(lèi)型。我們可以基于內(nèi)置的基本類(lèi)型定義,也可以通過(guò)struct定義。例如:

通過(guò)Type關(guān)鍵字的定義,MyInt就是一種新的類(lèi)型,它具有int的特性。

類(lèi)型別名

類(lèi)型別名是Go1.9版本添加的新功能。

類(lèi)型別名規(guī)定:TypeAlias只是Type的別名,本質(zhì)上TypeAlias與Type是同一個(gè)類(lèi)型。就像一個(gè)孩子小時(shí)候有小名、乳名,上學(xué)后用學(xué)名,英語(yǔ)老師又會(huì)給他起英文名,但這些名字都指的是他本人。

type TypeAlias = Type

我們之前見(jiàn)過(guò)的rune和byte就是類(lèi)型別名,他們的定義如下:

類(lèi)型定義和類(lèi)型別名的區(qū)別

類(lèi)型別名與類(lèi)型定義表面上看只有一個(gè)等號(hào)的差異,我們通過(guò)下面的這段代碼來(lái)理解它們之間的區(qū)別。

結(jié)果顯示a的類(lèi)型是main.NewInt,表示main包下定義的NewInt類(lèi)型。b的類(lèi)型是int。MyInt類(lèi)型只會(huì)在代碼中存在,編譯完成時(shí)并不會(huì)有MyInt類(lèi)型。

Go語(yǔ)言中的基礎(chǔ)數(shù)據(jù)類(lèi)型可以表示一些事物的基本屬性,但是當(dāng)我們想表達(dá)一個(gè)事物的全部或部分屬性時(shí),這時(shí)候再用單一的基本數(shù)據(jù)類(lèi)型明顯就無(wú)法滿足需求了,Go語(yǔ)言提供了一種自定義數(shù)據(jù)類(lèi)型,可以封裝多個(gè)基本數(shù)據(jù)類(lèi)型,這種數(shù)據(jù)類(lèi)型叫結(jié)構(gòu)體,英文名稱(chēng)struct。 也就是我們可以通過(guò)struct來(lái)定義自己的類(lèi)型了。

Go語(yǔ)言中通過(guò)struct來(lái)實(shí)現(xiàn)面向?qū)ο蟆?/p>

結(jié)構(gòu)體的定義

使用type和struct關(guān)鍵字來(lái)定義結(jié)構(gòu)體,具體代碼格式如下:

其中:

舉個(gè)例子,我們定義一個(gè)Person(人)結(jié)構(gòu)體,代碼如下:

同樣類(lèi)型的字段也可以寫(xiě)在一行,

這樣我們就擁有了一個(gè)person的自定義類(lèi)型,它有name、city、age三個(gè)字段,分別表示姓名、城市和年齡。這樣我們使用這個(gè)person結(jié)構(gòu)體就能夠很方便的在程序中表示和存儲(chǔ)人信息了。

語(yǔ)言?xún)?nèi)置的基礎(chǔ)數(shù)據(jù)類(lèi)型是用來(lái)描述一個(gè)值的,而結(jié)構(gòu)體是用來(lái)描述一組值的。比如一個(gè)人有名字、年齡和居住城市等,本質(zhì)上是一種聚合型的數(shù)據(jù)類(lèi)型

結(jié)構(gòu)體實(shí)例化

只有當(dāng)結(jié)構(gòu)體實(shí)例化時(shí),才會(huì)真正地分配內(nèi)存。也就是必須實(shí)例化后才能使用結(jié)構(gòu)體的字段。

基本實(shí)例化

舉個(gè)例子:

我們通過(guò).來(lái)訪問(wèn)結(jié)構(gòu)體的字段(成員變量),例如p1.name和p1.age等。

匿名結(jié)構(gòu)體

在定義一些臨時(shí)數(shù)據(jù)結(jié)構(gòu)等場(chǎng)景下還可以使用匿名結(jié)構(gòu)體。

創(chuàng)建指針類(lèi)型結(jié)構(gòu)體

我們還可以通過(guò)使用new關(guān)鍵字對(duì)結(jié)構(gòu)體進(jìn)行實(shí)例化,得到的是結(jié)構(gòu)體的地址。 格式如下:

從打印的結(jié)果中我們可以看出p2是一個(gè)結(jié)構(gòu)體指針。

需要注意的是在Go語(yǔ)言中支持對(duì)結(jié)構(gòu)體指針直接使用.來(lái)訪問(wèn)結(jié)構(gòu)體的成員。

取結(jié)構(gòu)體的地址實(shí)例化

使用對(duì)結(jié)構(gòu)體進(jìn)行取地址操作相當(dāng)于對(duì)該結(jié)構(gòu)體類(lèi)型進(jìn)行了一次new實(shí)例化操作。

p3.name = "七米"其實(shí)在底層是(*p3).name = "七米",這是Go語(yǔ)言幫我們實(shí)現(xiàn)的語(yǔ)法糖。

結(jié)構(gòu)體初始化

沒(méi)有初始化的結(jié)構(gòu)體,其成員變量都是對(duì)應(yīng)其類(lèi)型的零值。

使用鍵值對(duì)初始化

使用鍵值對(duì)對(duì)結(jié)構(gòu)體進(jìn)行初始化時(shí),鍵對(duì)應(yīng)結(jié)構(gòu)體的字段,值對(duì)應(yīng)該字段的初始值。

也可以對(duì)結(jié)構(gòu)體指針進(jìn)行鍵值對(duì)初始化,例如:

當(dāng)某些字段沒(méi)有初始值的時(shí)候,該字段可以不寫(xiě)。此時(shí),沒(méi)有指定初始值的字段的值就是該字段類(lèi)型的零值。

使用值的列表初始化

初始化結(jié)構(gòu)體的時(shí)候可以簡(jiǎn)寫(xiě),也就是初始化的時(shí)候不寫(xiě)鍵,直接寫(xiě)值:

使用這種格式初始化時(shí),需要注意:

結(jié)構(gòu)體內(nèi)存布局

結(jié)構(gòu)體占用一塊連續(xù)的內(nèi)存。

輸出:

【進(jìn)階知識(shí)點(diǎn)】關(guān)于Go語(yǔ)言中的內(nèi)存對(duì)齊推薦閱讀:在 Go 中恰到好處的內(nèi)存對(duì)齊

面試題

請(qǐng)問(wèn)下面代碼的執(zhí)行結(jié)果是什么?

構(gòu)造函數(shù)

Go語(yǔ)言的結(jié)構(gòu)體沒(méi)有構(gòu)造函數(shù),我們可以自己實(shí)現(xiàn)。 例如,下方的代碼就實(shí)現(xiàn)了一個(gè)person的構(gòu)造函數(shù)。 因?yàn)閟truct是值類(lèi)型,如果結(jié)構(gòu)體比較復(fù)雜的話,值拷貝性能開(kāi)銷(xiāo)會(huì)比較大,所以該構(gòu)造函數(shù)返回的是結(jié)構(gòu)體指針類(lèi)型。

調(diào)用構(gòu)造函數(shù)

方法和接收者

Go語(yǔ)言中的方法(Method)是一種作用于特定類(lèi)型變量的函數(shù)。這種特定類(lèi)型變量叫做接收者(Receiver)。接收者的概念就類(lèi)似于其他語(yǔ)言中的this或者 self。

方法的定義格式如下:

其中,

舉個(gè)例子:

方法與函數(shù)的區(qū)別是,函數(shù)不屬于任何類(lèi)型,方法屬于特定的類(lèi)型。

指針類(lèi)型的接收者

指針類(lèi)型的接收者由一個(gè)結(jié)構(gòu)體的指針組成,由于指針的特性,調(diào)用方法時(shí)修改接收者指針的任意成員變量,在方法結(jié)束后,修改都是有效的。這種方式就十分接近于其他語(yǔ)言中面向?qū)ο笾械膖his或者self。 例如我們?yōu)镻erson添加一個(gè)SetAge方法,來(lái)修改實(shí)例變量的年齡。

調(diào)用該方法:

值類(lèi)型的接收者

當(dāng)方法作用于值類(lèi)型接收者時(shí),Go語(yǔ)言會(huì)在代碼運(yùn)行時(shí)將接收者的值復(fù)制一份。在值類(lèi)型接收者的方法中可以獲取接收者的成員值,但修改操作只是針對(duì)副本,無(wú)法修改接收者變量本身。

什么時(shí)候應(yīng)該使用指針類(lèi)型接收者

任意類(lèi)型添加方法

在Go語(yǔ)言中,接收者的類(lèi)型可以是任何類(lèi)型,不僅僅是結(jié)構(gòu)體,任何類(lèi)型都可以擁有方法。 舉個(gè)例子,我們基于內(nèi)置的int類(lèi)型使用type關(guān)鍵字可以定義新的自定義類(lèi)型,然后為我們的自定義類(lèi)型添加方法。

注意事項(xiàng): 非本地類(lèi)型不能定義方法,也就是說(shuō)我們不能給別的包的類(lèi)型定義方法。

結(jié)構(gòu)體的匿名字段

匿名字段默認(rèn)采用類(lèi)型名作為字段名,結(jié)構(gòu)體要求字段名稱(chēng)必須唯一,因此一個(gè)結(jié)構(gòu)體中同種類(lèi)型的匿名字段只能有一個(gè)。

嵌套結(jié)構(gòu)體

一個(gè)結(jié)構(gòu)體中可以嵌套包含另一個(gè)結(jié)構(gòu)體或結(jié)構(gòu)體指針。

嵌套匿名結(jié)構(gòu)體

當(dāng)訪問(wèn)結(jié)構(gòu)體成員時(shí)會(huì)先在結(jié)構(gòu)體中查找該字段,找不到再去匿名結(jié)構(gòu)體中查找。

嵌套結(jié)構(gòu)體的字段名沖突

嵌套結(jié)構(gòu)體內(nèi)部可能存在相同的字段名。這個(gè)時(shí)候?yàn)榱吮苊馄缌x需要指定具體的內(nèi)嵌結(jié)構(gòu)體的字段。

結(jié)構(gòu)體的“繼承”

Go語(yǔ)言中使用結(jié)構(gòu)體也可以實(shí)現(xiàn)其他編程語(yǔ)言中面向?qū)ο蟮睦^承。

結(jié)構(gòu)體字段的可見(jiàn)性

結(jié)構(gòu)體中字段大寫(xiě)開(kāi)頭表示可公開(kāi)訪問(wèn),小寫(xiě)表示私有(僅在定義當(dāng)前結(jié)構(gòu)體的包中可訪問(wèn))。

結(jié)構(gòu)體與JSON序列化

JSON(JavaScript Object Notation) 是一種輕量級(jí)的數(shù)據(jù)交換格式。易于人閱讀和編寫(xiě)。同時(shí)也易于機(jī)器解析和生成。JSON鍵值對(duì)是用來(lái)保存JS對(duì)象的一種方式,鍵/值對(duì)組合中的鍵名寫(xiě)在前面并用雙引號(hào)""包裹,使用冒號(hào):分隔,然后緊接著值;多個(gè)鍵值之間使用英文,分隔。

結(jié)構(gòu)體標(biāo)簽(Tag)

Tag是結(jié)構(gòu)體的元信息,可以在運(yùn)行的時(shí)候通過(guò)反射的機(jī)制讀取出來(lái)。 Tag在結(jié)構(gòu)體字段的后方定義,由一對(duì)反引號(hào)包裹起來(lái),具體的格式如下:

`key1:"value1" key2:"value2"`

結(jié)構(gòu)體標(biāo)簽由一個(gè)或多個(gè)鍵值對(duì)組成。鍵與值使用冒號(hào)分隔,值用雙引號(hào)括起來(lái)。鍵值對(duì)之間使用一個(gè)空格分隔。 注意事項(xiàng): 為結(jié)構(gòu)體編寫(xiě)Tag時(shí),必須嚴(yán)格遵守鍵值對(duì)的規(guī)則。結(jié)構(gòu)體標(biāo)簽的解析代碼的容錯(cuò)能力很差,一旦格式寫(xiě)錯(cuò),編譯和運(yùn)行時(shí)都不會(huì)提示任何錯(cuò)誤,通過(guò)反射也無(wú)法正確取值。例如不要在key和value之間添加空格。

例如我們?yōu)镾tudent結(jié)構(gòu)體的每個(gè)字段定義json序列化時(shí)使用的Tag:

go語(yǔ)言語(yǔ)法(基礎(chǔ)語(yǔ)法篇)

import "workname/packetfolder"

導(dǎo)入多個(gè)包

方法調(diào)用 包名.函數(shù)//不是函數(shù)或結(jié)構(gòu)體所處文件或文件夾名

packagename.Func()

前面加個(gè)點(diǎn)表示省略調(diào)用,那么調(diào)用該模塊里面的函數(shù),可以不用寫(xiě)模塊名稱(chēng)了:

當(dāng)導(dǎo)入一個(gè)包時(shí),該包下的文件里所有init()函數(shù)都會(huì)被執(zhí)行,然而,有些時(shí)候我們并不需要把整個(gè)包都導(dǎo)入進(jìn)來(lái),僅僅是是希望它執(zhí)行init()函數(shù)而已。下劃線的作用僅僅是為了調(diào)用init()函數(shù),所以無(wú)法通過(guò)包名來(lái)調(diào)用包中的其他函數(shù)

import _ package

變量聲明必須要使用否則會(huì)報(bào)錯(cuò)。

全局變量運(yùn)行聲明但不使用。

func 函數(shù)名 (參數(shù)1,參數(shù)2,...) (返回值a 類(lèi)型a, 返回值b 類(lèi)型b,...)

func 函數(shù)名 (參數(shù)1,參數(shù)2,...) (返回值類(lèi)型1, 返回值類(lèi)型2,...)

func (this *結(jié)構(gòu)體名) 函數(shù)名(參數(shù) string) (返回值類(lèi)型1, 返回值類(lèi)型2){}

使用大小來(lái)區(qū)分函數(shù)可見(jiàn)性

大寫(xiě)是public類(lèi)型

小寫(xiě)是private類(lèi)型

func prifunc int{}

func pubfunc int{}

聲明靜態(tài)變量

const value int

定義變量

var value int

聲明一般類(lèi)型、接口和結(jié)構(gòu)體

聲明函數(shù)

func function () int{}

go里面所有的空值對(duì)應(yīng)如下

通道類(lèi)型

內(nèi)建函數(shù) new 用來(lái)分配內(nèi)存,它的第一個(gè)參數(shù)是一個(gè)類(lèi)型,不是一個(gè)值,它的返回值是一個(gè)指向新分配類(lèi)型零值的指針

func new(Type) *Type

[這位博主有非常詳細(xì)的分析]

Go 語(yǔ)言支持并發(fā),我們只需要通過(guò) go 關(guān)鍵字來(lái)開(kāi)啟 goroutine 即可。

goroutine 是輕量級(jí)線程,goroutine 的調(diào)度是由 Golang 運(yùn)行時(shí)進(jìn)行管理的。

同一個(gè)程序中的所有 goroutine 共享同一個(gè)地址空間。

語(yǔ)法格式如下:

通道(channel)是用來(lái)傳遞數(shù)據(jù)的一個(gè)數(shù)據(jù)結(jié)構(gòu)。

通道的聲明

通道可用于兩個(gè) goroutine 之間通過(guò)傳遞一個(gè)指定類(lèi)型的值來(lái)同步運(yùn)行和通訊。操作符 - 用于指定通道的方向,發(fā)送或接收。如果未指定方向,則為雙向通道。

[這里有比較詳細(xì)的用例]

go里面的空接口可以指代任何類(lèi)型(無(wú)論是變量還是函數(shù))

聲明空接口

go里面的的強(qiáng)制類(lèi)型轉(zhuǎn)換語(yǔ)法為:

int(data)

如果是接口類(lèi)型的強(qiáng)制轉(zhuǎn)成其他類(lèi)型的語(yǔ)法為:

go里面的強(qiáng)制轉(zhuǎn)換是將值復(fù)制過(guò)去,所以在數(shù)據(jù)量的時(shí)候有比較高的運(yùn)行代價(jià)

為什么要使用 Go 語(yǔ)言?Go 語(yǔ)言的優(yōu)勢(shì)在哪里

1、學(xué)習(xí)曲線

它包含了類(lèi)C語(yǔ)法、GC內(nèi)置和工程工具。這一點(diǎn)非常重要,因?yàn)镚o語(yǔ)言容易學(xué)習(xí),所以一個(gè)普通的大學(xué)生花一個(gè)星期就能寫(xiě)出來(lái)可以上手的、高性能的應(yīng)用。在國(guó)內(nèi)大家都追求快,這也是為什么國(guó)內(nèi)Go流行的原因之一。

2、效率

Go擁有接近C的運(yùn)行效率和接近PHP的開(kāi)發(fā)效率,這就很有利的支撐了上面大家追求快速的需求。

3、出身名門(mén)、血統(tǒng)純正

之所以說(shuō)Go語(yǔ)言出身名門(mén),是因?yàn)槲覀冎繥o語(yǔ)言出自Google公司,這個(gè)公司在業(yè)界的知名度和實(shí)力自然不用多說(shuō)。Google公司聚集了一批牛人,在各種編程語(yǔ)言稱(chēng)雄爭(zhēng)霸的局面下推出新的編程語(yǔ)言,自然有它的戰(zhàn)略考慮。而且從Go語(yǔ)言的發(fā)展態(tài)勢(shì)來(lái)看,Google對(duì)它這個(gè)新的寵兒還是很看重的,Go自然有一個(gè)良好的發(fā)展前途。我們看看Go語(yǔ)言的主要?jiǎng)?chuàng)造者,血統(tǒng)純正這點(diǎn)就可見(jiàn)端倪了。

4、組合的思想、無(wú)侵入式的接口

Go語(yǔ)言可以說(shuō)是開(kāi)發(fā)效率和運(yùn)行效率二者的完美融合,天生的并發(fā)編程支持。Go語(yǔ)言支持當(dāng)前所有的編程范式,包括過(guò)程式編程、面向?qū)ο缶幊桃约昂瘮?shù)式編程。

5、強(qiáng)大的標(biāo)準(zhǔn)庫(kù)

這包括互聯(lián)網(wǎng)應(yīng)用、系統(tǒng)編程和網(wǎng)絡(luò)編程。Go里面的標(biāo)準(zhǔn)庫(kù)基本上已經(jīng)是非常穩(wěn)定,特別是我這里提到的三個(gè),網(wǎng)絡(luò)層、系統(tǒng)層的庫(kù)非常實(shí)用。

6、部署方便

我相信這一點(diǎn)是很多人選擇Go的最大理由,因?yàn)椴渴鹛奖悖袁F(xiàn)在也有很多人用Go開(kāi)發(fā)運(yùn)維程序。

7、簡(jiǎn)單的并發(fā)

它包含降低心智的并發(fā)和簡(jiǎn)易的數(shù)據(jù)同步,我覺(jué)得這是Go最大的特色。之所以寫(xiě)正確的并發(fā)、容錯(cuò)和可擴(kuò)展的程序如此之難,是因?yàn)槲覀冇昧隋e(cuò)誤的工具和錯(cuò)誤的抽象,Go可以說(shuō)這一塊做的相當(dāng)簡(jiǎn)單。

8、穩(wěn)定性

Go擁有強(qiáng)大的編譯檢查、嚴(yán)格的編碼規(guī)范和完整的軟件生命周期工具,具有很強(qiáng)的穩(wěn)定性,穩(wěn)定壓倒一切。那么為什么Go相比于其他程序會(huì)更穩(wěn)定呢?這是因?yàn)镚o提供了軟件生命周期的各個(gè)環(huán)節(jié)的工具,如go

tool、gofmt、go test。

Go語(yǔ)言”奇怪用法“有哪些

1,go的變量聲明順序是:”先寫(xiě)變量名,再寫(xiě)類(lèi)型名“,此與C/C++的語(yǔ)法孰優(yōu)孰劣,可見(jiàn)下文解釋?zhuān)?/p>

2,go是通過(guò)package來(lái)組織的(與python類(lèi)似),只有package名為main的包可以包含main函數(shù),一個(gè)可執(zhí)行程序有且僅有一個(gè)main包,通過(guò)import關(guān)鍵字來(lái)導(dǎo)入其他非main包。

3,可見(jiàn)性規(guī)則。go語(yǔ)言中,使用大小寫(xiě)來(lái)決定該常量、變量、類(lèi)型、接口、結(jié)構(gòu)或函數(shù)是否可以被外部包含調(diào)用。根據(jù)約定,函數(shù)名首字母小寫(xiě)即為private,函數(shù)名首字母大寫(xiě)即為public。

4,go內(nèi)置關(guān)鍵字(25個(gè)均為小寫(xiě))。

5,函數(shù)不用先聲明,即可使用。

6,在函數(shù)內(nèi)部可以通過(guò) := 隱士定義變量。(函數(shù)外必須顯示使用var定義變量)

7,go程序使用UTF-8編碼的純Unicode文本編寫(xiě)。

8,使用big.Int的陷阱:

9,從技術(shù)層面講,go語(yǔ)言的語(yǔ)句是以分號(hào)分隔的,但這些是由編譯器自動(dòng)添加的,不用手動(dòng)輸入,除非需要在同一行中寫(xiě)入多個(gè)語(yǔ)句。沒(méi)有分號(hào)及只需少量的逗號(hào)和圓括號(hào),使得go語(yǔ)言的程序更容易閱讀。

10,go語(yǔ)言只有一個(gè)循環(huán)結(jié)構(gòu)——for循環(huán)。

11,go里的自增運(yùn)算符只有——“后++”

12,go語(yǔ)言中的slice用法類(lèi)似python中數(shù)組,關(guān)于slice的詳細(xì)用法可見(jiàn):

13,函數(shù)也是一個(gè)值,使用匿名函數(shù)返回一個(gè)值。

14,函數(shù)閉包的使用,閉包是一個(gè)匿名函數(shù)值,會(huì)引用到其外部的變量。

Go 內(nèi)存模型 并發(fā)可見(jiàn)性

Go內(nèi)存模型指定了在何種條件下可以保證在一個(gè) goroutine 中讀取變量時(shí)觀察到不同 goroutine 中寫(xiě)入該變量的值。

通過(guò)多個(gè)協(xié)程并發(fā)修改數(shù)據(jù)的程序必須將操作序列化。為了序列化訪問(wèn),通過(guò)channel操作或者其他同步原語(yǔ)( sync 、 sync/atomic )來(lái)保護(hù)數(shù)據(jù)。

如果你必須要閱讀本文的其他部分才能理解你程序的行為,請(qǐng)盡量不要這樣...

在單個(gè) goroutine 中,讀取和寫(xiě)入的行為必須像按照程序指定的順序執(zhí)行一樣。 也就是說(shuō),只有當(dāng)重新排序不會(huì)改變語(yǔ)言規(guī)范定義的 goroutine 中的行為時(shí),編譯器和處理器才可以重新排序在單個(gè) goroutine 中執(zhí)行的讀取和寫(xiě)入。 由于這種重新排序,一個(gè) goroutine 觀察到的執(zhí)行順序可能與另一個(gè) goroutine 感知的順序不同。 例如,如果一個(gè) goroutine 執(zhí)行 a = 1; b = 2;,另一個(gè)可能會(huì)在 a 的更新值之前觀察到 b 的更新值。

為了滿足讀寫(xiě)的需求,我們定義了 happens before ,Go程序中內(nèi)存操作的局部順序。如果事件 e1 在 e2 之前發(fā)生,我們說(shuō) e2 在 e1 之后發(fā)生。還有,如果 e1 不在 e2 之前發(fā)生、 e2 也不在 e1 之前發(fā)生,那么我們說(shuō) e1 和 e2 并發(fā)happen。

在單個(gè) goroutine 中, happens-before 順序由程序指定。

當(dāng)下面兩個(gè)條件滿足時(shí),變量 v 的閱讀操作 r 就 可能 觀察到寫(xiě)入操作 w

為了保證 r 一定能閱讀到 v ,保證 w 是 r 能觀測(cè)到的唯一的寫(xiě)操作。當(dāng)下面兩個(gè)條件滿足時(shí), r 保證可以讀取到 w

這一對(duì)條件比上一對(duì)條件更強(qiáng);這要求無(wú)論是 w 還是 r ,都沒(méi)有相應(yīng)的并發(fā)操作。

在單個(gè) goroutine 中,沒(méi)有并發(fā)。所以這兩個(gè)定義等價(jià):讀操作 r 能讀到最近一次 w 寫(xiě)入 v 的值。但是當(dāng)多個(gè) goroutine 訪問(wèn)共享變量時(shí),它們必須使用同步事件來(lái)建立 happens-before 關(guān)系。

使用變量 v 類(lèi)型的0值初始化變量 v 的行為類(lèi)似于內(nèi)存模型中的寫(xiě)入。

對(duì)于大于單個(gè)機(jī)器字長(zhǎng)的值的讀取和寫(xiě)入表現(xiàn)為未指定順序的對(duì)多個(gè)機(jī)器字長(zhǎng)的操作。

程序初始化在單個(gè) goroutine 中運(yùn)行,但該 goroutine 可能會(huì)創(chuàng)建其他并發(fā)運(yùn)行的 goroutine。

如果包 p 導(dǎo)入包 q,則 q 的 init 函數(shù)的完成發(fā)生在任何 p 的操作開(kāi)始之前。

main.main 函數(shù)的啟動(dòng)發(fā)生在所有 init 函數(shù)完成之后。

go 語(yǔ)句啟動(dòng)新的協(xié)程發(fā)生在新協(xié)程啟動(dòng)開(kāi)始之前。

舉個(gè)例子

調(diào)用 hello 將會(huì)打印 hello, world 。當(dāng)然,這個(gè)時(shí)候 hello 可能已經(jīng)返回了。

go 協(xié)程的退出并不保證發(fā)生在任何事件之前

對(duì) a 的賦值之后沒(méi)有任何同步事件,因此不能保證任何其他 goroutine 都會(huì)觀察到它。 事實(shí)上,激進(jìn)的編譯器可能會(huì)刪除整個(gè) go 語(yǔ)句。

如果一個(gè) goroutine 的效果必須被另一個(gè) goroutine 觀察到,請(qǐng)使用同步機(jī)制,例如鎖或通道通信來(lái)建立相對(duì)順序。

通道通信是在go協(xié)程之間傳輸數(shù)據(jù)的主要手段。在特定通道上的發(fā)送總有一個(gè)對(duì)應(yīng)的channel的接收,通常是在另外一個(gè)協(xié)程。

channel 上的發(fā)送發(fā)生在對(duì)應(yīng) channel 接收之前

程序能保證輸出 hello, world 。對(duì)a的寫(xiě)入發(fā)生在往 c 發(fā)送數(shù)據(jù)之前,往 c 發(fā)送數(shù)據(jù)又發(fā)生在從 c 接收數(shù)據(jù)之前,它又發(fā)生在 print 之前。

channel 的關(guān)閉發(fā)生在從 channel 中獲取到0值之前

在之前的例子中,將 c-0 替換為 close(c) ,程序還是能保證輸出 hello, world

無(wú)buffer channel 的接收發(fā)生在發(fā)送操作完成之前

這個(gè)程序,和之前一樣,但是調(diào)換發(fā)送和接收操作,并且使用無(wú)buffer的channel

也保證能夠輸出 hello, world 。對(duì)a的寫(xiě)入發(fā)生在c的接收之前,繼而發(fā)生在c的寫(xiě)入操作完成之前,繼而發(fā)生在print之前。

如果該 channel 是buffer channel (例如: c=make(chan int, 1) ),那么程序就不能保證輸出 hello, world ??赡軙?huì)打印空字符串、崩潰等等。從而,我們得到一個(gè)相對(duì)通用的推論:

對(duì)于容量為C的buffer channel來(lái)說(shuō),第k次從channel中接收,發(fā)生在第 k + C 次發(fā)送完成之前。

此規(guī)則將先前的規(guī)則推廣到緩沖通道。 它允許通過(guò)buffer channel 來(lái)模擬信號(hào)量:通道中的條數(shù)對(duì)應(yīng)活躍的數(shù)量,通道的容量對(duì)應(yīng)于最大并發(fā)數(shù)。向channel發(fā)送數(shù)據(jù)相當(dāng)于獲取信號(hào)量,從channel中接收數(shù)據(jù)相當(dāng)于釋放信號(hào)量。 這是限制并發(fā)的常用習(xí)慣用法。

該程序?yàn)楣ぷ髁斜碇械拿總€(gè)條目啟動(dòng)一個(gè) goroutine,但是 goroutine 使用 limit channel進(jìn)行協(xié)調(diào),以確保一次最多三個(gè)work函數(shù)正在運(yùn)行。

sync 包中實(shí)現(xiàn)了兩種鎖類(lèi)型: sync.Mutex 和 sync.RWMutex

對(duì)于任何的 sync.Mutex 或者 sync.RWMutex 變量 ,且有 nm ,第 n 個(gè)調(diào)用 UnLock 一定發(fā)生在 m 個(gè) Lock`之前。

這個(gè)程序也保證輸出 hello,world 。第一次調(diào)用 unLock 一定發(fā)生在第二次 Lock 調(diào)用之前

對(duì)于任何 sync.RWMutex 的 RLock 方法調(diào)用,存在變量n,滿足 RLock 方法發(fā)生在第 n 個(gè) UnLock 調(diào)用之后,并且對(duì)應(yīng)的 RUnlock 發(fā)生在第 n+1 個(gè) Lock 方法之前。

在存在多個(gè) goroutine 時(shí), sync 包通過(guò) once 提供了一種安全的初始化機(jī)制。對(duì)于特定的 f ,多個(gè)線程可以執(zhí)行 once.Do(f) ,但是只有一個(gè)會(huì)運(yùn)行 f() ,另一個(gè)調(diào)用會(huì)阻塞,直到 f() 返回

從 once.Do(f) 對(duì) f() 的單個(gè)調(diào)用返回在任何一個(gè) once.Do(f) 返回之前。

調(diào)用 twoprint 將只調(diào)用一次 setup。 setup 函數(shù)將在任一打印調(diào)用之前完成。 結(jié)果將是 hello, world 打印兩次。

注意,讀取 r 有可能觀察到了由寫(xiě)入 w 并發(fā)寫(xiě)入的值。盡管觀察到了這個(gè)值,也并不意味著 r 后續(xù)的讀取可以讀取到 w 之前的寫(xiě)入。

有可能 g 會(huì)接連打印2和0兩個(gè)值。

雙檢查鎖是為了降低同步造成的開(kāi)銷(xiāo)。舉個(gè)例子, twoprint 方法可能會(huì)被誤寫(xiě)成

因?yàn)闆](méi)有任何機(jī)制保證,協(xié)程觀察到done為true的同時(shí)可以觀測(cè)到a為 hello, world ,其中有一個(gè) doprint 可能會(huì)輸出空字符。

另外一個(gè)例子

和以前一樣,不能保證在 main 中,觀察對(duì) done 的寫(xiě)入意味著觀察對(duì) a 的寫(xiě)入,因此該程序也可以打印一個(gè)空字符串。 更糟糕的情況下,由于兩個(gè)線程之間沒(méi)有同步事件,因此無(wú)法保證 main 會(huì)觀察到對(duì) done 的寫(xiě)入。 main 中的循環(huán)會(huì)一直死循環(huán)。

下面是該例子的一個(gè)更微妙的變體

盡管 main 觀測(cè)到g不為nil,但是也沒(méi)有任何機(jī)制保證可以讀取到t.msg。

在上述例子中,解決方案都是相同的:請(qǐng)使用顯式的同步機(jī)制。


網(wǎng)頁(yè)名稱(chēng):go語(yǔ)言可見(jiàn)性 go語(yǔ)言可視化編程
當(dāng)前鏈接:http://weahome.cn/article/doodscd.html

其他資訊

在線咨詢(xún)

微信咨詢(xún)

電話咨詢(xún)

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部