0

我正在尝试使用 Docker 的 Golang SDK 来维护一个切片变量,其中包含当前在本地 Docker 实例上运行的容器。这个切片是从一个包中导出的,我想用它来提供一个网页。

我真的不习惯 goroutine 和通道,这就是为什么我想知道我是否找到了解决问题的好方法。

我有一个docker包如下。

https://play.golang.org/p/eMmqkMezXZn

它有一个Running变量,其中包含正在运行的容器的当前状态。

var Running []types.Container

我使用一个reload函数将正在运行的容器加载到Running变量中。

// Reload the list of running containers
func reload() error {
    ...
    Running, err = cli.ContainerList(context.Background(), types.ContainerListOptions{
        All: false,
    })
    ...
}

然后我从init函数中启动一个 goroutine 来监听 Docker 事件并相应地触发reload函数。

func init() {
    ...
    // Listen for docker events
    go listen()
    ...
}

// Listen for docker events
func listen() {
    filter := filters.NewArgs()
    filter.Add("type", "container")
    filter.Add("event", "start")
    filter.Add("event", "die")

    msg, errChan := cli.Events(context.Background(), types.EventsOptions{
        Filters: filter,
    })

    for {
        select {
        case err := <-errChan:
            panic(err)
        case <-msg:
            fmt.Println("reloading")
            reload()
        }
    }
}

我的问题是,从 goroutine 内部更新变量是否合适(就同步而言)?也许有一种更清洁的方式来实现我想要构建的东西?

更新

我在这里关心的并不是缓存。更多的是从 Docker SDK 中隐藏监听和更新过程的“复杂性”。我想提供类似索引的东西,让最终用户轻松循环并显示当前正在运行的容器。

我知道线程程序中的数据竞争问题,但我没有意识到我实际上是在并发上下文中(我以前从未在 Go 中编写过并发程序)。

我实际上需要重新考虑解决方案以使其更加惯用。据我所知,我有两个选择:要么用互斥锁保护变量,要么重新考虑设计以集成通道。

对我来说最重要的是隐藏或封装所使用的同步方法,这样包用户就不必担心共享状态是如何受到保护的。

你有什么建议吗?

非常感谢你的帮助,洛瑞克

4

1 回答 1

0

Running不,在两个 goroutine 之间共享变量不是惯用的 Go 。你可以通过在运行你的main函数的例程和listen启动的函数之间共享它来做到这一点——它会go产生另一个 goroutine。

为什么,是因为它与

不要通过共享内存进行通信;相反,通过通信共享内存。¹

所以 API 的设计需要改变才能符合习惯;您需要删除Running变量并将其替换为什么?这取决于您要达到的目标。cli.ContainerList如果您因为需要经常调用它而尝试缓存,并且它可能很昂贵,您应该实现一个缓存,该缓存在每个cli.Events.

你的动机是什么?

于 2018-10-16T06:24:33.337 回答