当我在Goroutine中填充它时,为什么这张地图是空的?

时间:2016-06-27 10:36:57

标签: go struct reference slice 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[]

Playground

3 个答案:

答案 0 :(得分:4)

问题很简单:你有一片driver s:

var Drivers []driver

请注意,Drivers是一些结构类型的切片,而不是指针的一部分!

当您附加某些内容(或者为其中一个元素指定值)时:

Drivers = append(Drivers, driver)

这会附加(或分配)值的副本!所以当你稍后这样做时:

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

它会将新地图值设置为driver.variables,但这与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个结构值,它们也具有相同的地图值(相同的地图标题指向相同的地图数据结构)。

如果您在结构值driver.populate()上调用Drivers[0](并坚持打印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]将是相同的指针(因为初始地图不再可访问,所以只有一个结构值和一个地图值)。请在Go Playground上尝试此操作。

答案 1 :(得分:0)

使用goroutine版本你没有得到未初始化的地图的原因是当主函数返回时,程序退出:它不等待其他(非主要)goroutines完成 。请注意,主要功能本身就是goroutine。

所以即使你使用:

初始化地图
driver.variables = make(map[string]string)

这并不意味着您实际填充了值,您只是初始化哈希映射数据结构并返回指向它的映射值。

  

地图类型是引用类型,如指针或切片,等等   m以上的值为零;它没有指向初始化的地图。没有   在阅读时,map的行为类似于空地图,但尝试写入   零映射会导致运行时恐慌;不要那样做。初始化一个   map,使用内置的make函数。

如果您先删除go关键字,则会初始化driver.variables地图。但是因为它在同一个线程(主线程)中运行,并且你首先在populate函数上放置了一个时间延迟,它会初始化地图,然后填充它。

答案 2 :(得分:0)

我最好使用频道代替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
}

Playground