当我在 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[]

Playground

使用goroutine版本没有得到未初始化映射的原因是当main函数returns,程序退出:它不等待其他(非main) ) goroutines 完成。请注意 main 函数本身是一个 goroutine。

因此,即使您使用以下方式初始化地图:

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

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

Map types are reference types, like pointers or slices, and so the value of m above is nil; it doesn't point to an initialized map. A nil map behaves like an empty map when reading, but attempts to write to a nil map will cause a runtime panic; don't do that. To initialize a map, use the built in make function.

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

我最好使用频道而不是 sleeps:

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

问题很简单:你有一片 drivers:

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 个结构值,它们具有相同的映射值(相同的映射 header 指向相同的映射数据结构)。

如果你在结构值 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] 将是同一个指针(您将只有一个结构值和一个映射值,因为初始映射不再可访问)。在 Go Playground.

上试试这个