簡體   English   中英

當我在Goroutine中填充它時,為什么這張地圖是空的?

[英]Why is this map empty when I populate it in a Goroutine?

type driver struct {
    variables map[string]string
}

var Drivers []driver

func main() {

    driver := driver{
        variables: make(map[string]string),
    }
    Drivers = append(Drivers, driver)

    driver.variables = make(map[string]string) // Commenting this line makes it work, too

    done := make(chan bool) 
    go driver.populate(done)

    <-done

    fmt.Print(Drivers[0].variables)
}

func (this *driver) populate(done chan bool) {
    time.Sleep(500 * time.Millisecond)
    this.variables["a"] = "b"
    done <- true
}

我期望:

map[a:b]

實際結果:

map[]

操場

問題很簡單:你有一片driver

var Drivers []driver

請注意, Drivers是一些結構類型的片段,而不是指針的一部分!

附加內容時(或者為其中一個元素賦值):

Drivers = append(Drivers, driver)

這會附加(或分配)值的副本! 所以當你以后這樣做時:

driver.variables = make(map[string]string)

它將為driver.variables設置一個新的map值,但這與存儲在Drivers的值不同(更准確地說是在Drivers[0] )。

稍后您將填充driver.variables ,但您打印Drivers[0].variables 它們是2個不同的結構值,具有2個不同的地圖值。 Goroutines在這里沒有發揮作用(他們正確地同步,所以他們不應該這樣做)。

你會打印driver.variables

fmt.Print(driver.variables)

你會看到(在Go Playground上試試):

map[a:b]

如果你注釋掉這一行:

driver.variables = make(map[string]string) // Commenting this line makes it work, too

它可以工作, 但只是因為即使你有2個struct值,它們也有相同的map值(相同的map頭指向同一個map數據結構)。

如果在結構值Drivers[0]上調用driver.populate() (並且堅持打印Drivers[0].variables ),也可以使它工作:

go Drivers[0].populate(done)

// ...

fmt.Print(Drivers[0].variables)

Go Playground嘗試這個。

如果Drivers是一個指針,你也可以使它工作:

var Drivers []*driver

// ...

driver := &driver{
    variables: make(map[string]string),
}

因為driverDriver[0]將是相同的指針(因為初始映射不再可訪問,所以只有一個struct值和一個map值)。 Go Playground嘗試這個。

使用goroutine版本你沒有獲得未初始化的映射的原因是當main函數返回時,程序退出: 它不等待其他(非主要)goroutine完成 請注意主要功能本身是goroutine。

因此,即使您使用以下內容初始化地圖:

driver.variables = make(map[string]string)

它並不意味着您實際填充值,您只是初始化哈希映射數據結構並返回指向它的映射值。

地圖類型是引用類型,如指針或切片,因此上面的m值為nil; 它沒有指向初始化的地圖。 在讀取時,nil映射的行為類似於空映射,但嘗試寫入nil映射將導致運行時出現混亂。 不要那樣做。 要初始化地圖,請使用內置的make函數。

如果您首先刪除go關鍵字,它將初始化driver.variables地圖。 但是因為它在同一個線程(主線程)中運行,並且你首先在populate函數上放置一個時間延遲,它將初始化地圖,然后填充它。

我最好使用頻道而不是sleep s:

package main

import (
    "fmt"
    "time"
)

type driver struct {
    variables map[string]string
}

var Drivers []driver

func main() {
    driver := driver{
        variables: make(map[string]string),
    }
    Drivers = append(Drivers, driver)

    done := make(chan bool)
    go driver.populate(done)
    <-done // wait for goroutine to complete

    fmt.Print(Drivers[0].variables)
}

func (this *driver) populate(done chan bool) {
    time.Sleep(500 * time.Millisecond)
    this.variables["a"] = "b"
    done <- true
}

操場

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM