自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

理解Go中空結(jié)構(gòu)體的應用和實現(xiàn)原理

開發(fā) 前端
空結(jié)構(gòu)體是一種不包含任何字段的結(jié)構(gòu)體類型,不僅具有結(jié)構(gòu)體類型的一切屬性,而且該結(jié)構(gòu)體類型占用的空間為0。常被用于map的集合或和通道配合使用發(fā)送信號使用的場景。

在實際項目或開源程序中,相信大家都見過將一個空結(jié)構(gòu)體作為map值的場景:

// CanSkipFuncs will skip valid if RequiredFirst is true and the struct field's value is empty
var CanSkipFuncs = map[string]struct{}{
    "Email":   {},
    "IP":      {},
    "Mobile":  {},
    "Tel":     {},
    "Phone":   {},
    "ZipCode": {},
}

或?qū)⒁粋€空結(jié)構(gòu)體寫入到通道中的使用:

w.ch <- struct{}{}

那為什么要這樣使用空結(jié)構(gòu)體呢?今天就跟大家一起來學習下空結(jié)構(gòu)體的應用以及底層原理。

1 什么空結(jié)構(gòu)體

首先來看看空結(jié)構(gòu)體是什么。空結(jié)構(gòu)體也是結(jié)構(gòu)體類型,具有結(jié)構(gòu)體的一切特性。但該結(jié)構(gòu)體中沒有任何字段組合。所以,該空結(jié)構(gòu)體類型的變量占用的空間為0。

我們通過unsafe.Sizeof函數(shù)來驗證一下。unsafe.Sizeof函數(shù)的作用是返回一個數(shù)據(jù)類型所占的空間大小。我們驗證一下:

var s struct{}
fmt.Println(unsafe.Sizeof(s)) // prints 0

我們看到打印的結(jié)果是0,表明struct{}的類型占用的空間是0。

我們還可以通過reflect的類型來驗證。

var s struct{}
typ := reflect.TypeOf(s)
fmt.Println(typ.Size()) // 0

我們看到,通過映射變量s的類型,輸出空類型的空間大小也是0。

2 空結(jié)構(gòu)體類型變量的地址

我們知道,在編程語言中,變量的作用就是在內(nèi)存中,標記和存儲數(shù)據(jù)的。也就是說每個變量會對應著一塊內(nèi)存空間,既然是內(nèi)存空間,那就應該有對應的內(nèi)存地址。那空結(jié)構(gòu)體類型變量的地址是什么呢?我們通過如下代碼來看下:

package main
 
import (
    "fmt"
    "unsafe"
)
 
type emptyStruct struct{}
 
func main() {
    a := struct{}{}
    b := struct{}{}
 
    c := emptyStruct{}
 
    fmt.Println(a)
    fmt.Printf("%pn", &a) //0x116be80
    fmt.Printf("%pn", &b) //0x116be80
    fmt.Printf("%pn", &c) //0x116be80
 
    fmt.Println(a == b) //true
}

我們發(fā)現(xiàn),所有空結(jié)構(gòu)體類型的變量地址都是一樣的。那這是為什么呢?

在底層實現(xiàn)中,這和一個很重要的 zerobase 變量有關(guān)(在runtime里多次使用到了這個變量),而zerobase 變量是一個 uintptr 的全局變量,占用8個字節(jié)。在go源碼src/runtime/malloc.go中有如下定義:

// base address for all 0-byte allocations
var zerobase uintptr

只要你將struct{} 賦值給一個或者多個變量,它都返回這個 zerobase 的地址,這點我們上面已經(jīng)證實過這一點了。

在golang中大量的地方使用到了這個 zerobase 變量,只要分配的內(nèi)存為0,就返回這個變量地址,在go源碼src/runtime/malloc.go的mallocgc函數(shù)中定義如下:

func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
    if gcphase == _GCmarktermination {
  throw("mallocgc called with gcphase == _GCmarktermination")
    }


    if size == 0 {
  return unsafe.Pointer(&zerobase)
    }
    ...
}

3 空結(jié)構(gòu)體的應用場景

一般我們用在用戶不關(guān)注值內(nèi)容的情況下,只是作為一個信號或一個占位符來使用。

  • 基于map實現(xiàn)集合功能。
  • 與channel組合使用,實現(xiàn)一個信號

基于map實現(xiàn)集合功能就是我們開頭提到的。使用空結(jié)構(gòu)體不占用存儲空間外,還有一個語義上的原因。例如:

var CanSkipFuncs = map[string]bool{
    "Email":   true,
    "IP":      true,
    "Mobile":  true,
    "Tel":     false,
    "Phone":   false,
    "ZipCode": false,
}

我們這里將空結(jié)構(gòu)體類型更換成布爾類型。首先,聲明下,CanSkipFuncs集合代表的是所有要跳過的函數(shù)。所以這里的值設置成true還是false是沒有任何影響的。

那么當閱讀或review代碼的時候,很有可能帶來疑惑,對于值所表達的意圖就有所懷疑,增加了理解代碼的難度。就會理解成當值為true時會執(zhí)行一個分支,當值為false時會執(zhí)行另一段邏輯。而相比使用一個空結(jié)構(gòu)體strcut{}理解起來會更容易,一看空結(jié)構(gòu)體struct{}就知道要表達的意思是不需要關(guān)心值是什么,只需要關(guān)心鍵值即可。

我們再來看下和channel組合使用的例子。在etcd項目中,就有通過往channel中寫入一個空結(jié)構(gòu)體作為信號的,源碼位于/etcd/server/auth/simple_token.go中,如下:

func (tm *simpleTokenTTLKeeper) stop() {
    select {
    case tm.stopc <- struct{}{}:
    case <-tm.donec:
    }
    <-tm.donec
}

還有一種是基于緩沖channel實現(xiàn)并發(fā)限速。如下:

var limit = make(chan struct{}, 3)


func main() {
    // …………
    for _, w := range work {
        go func() {
            limit <- struct{}{}
            w()
            <-limit
        }()
    }
    // …………
}

4 總結(jié)

空結(jié)構(gòu)體是一種不包含任何字段的結(jié)構(gòu)體類型,不僅具有結(jié)構(gòu)體類型的一切屬性,而且該結(jié)構(gòu)體類型占用的空間為0。常被用于map的集合或和通道配合使用發(fā)送信號使用的場景。

參考鏈接:

https://blog.haohtml.com/archives/20339

https://ijayer.github.io/post/tech/code/golang/20200419_emtpy_struct_in_go/

責任編輯:武曉燕 來源: 大話開源
相關(guān)推薦

2021-11-02 12:19:18

Go函數(shù)結(jié)構(gòu)

2020-12-02 09:10:22

Go結(jié)構(gòu)數(shù)據(jù)類型

2021-04-20 09:00:48

Go 語言結(jié)構(gòu)體type

2023-07-29 15:03:29

2024-08-14 18:18:47

2021-11-02 14:54:41

Go結(jié)構(gòu)體標簽

2023-11-21 08:03:43

語言架構(gòu)偏移量

2020-11-23 08:54:14

Go語言結(jié)構(gòu)體

2022-10-24 00:48:58

Go語言errgroup

2020-11-30 06:17:03

Go語言

2020-11-26 06:40:24

Go語言基礎

2020-12-02 08:45:36

Go語言

2021-12-20 07:59:07

Go語言結(jié)構(gòu)體

2020-08-10 15:24:05

Snowflake算法開源

2009-08-13 14:24:44

C#結(jié)構(gòu)體構(gòu)造函數(shù)

2022-05-06 09:22:25

Go泛型

2022-07-06 08:30:36

vuereactvdom

2024-01-02 10:54:07

Rust結(jié)構(gòu)體元組

2011-04-11 13:00:08

C++結(jié)構(gòu)體枚舉

2023-10-25 12:51:28

Go調(diào)度器
點贊
收藏

51CTO技術(shù)棧公眾號