在他对这个问题的回答中:Golang for Windows erratic behavior? 建议USER@DISTRIBTED锁定/同步访问并发Goroutine上的共享变量.

我怎么才能做到这一点呢?

有关这个问题的更多信息:

我让这段代码(闭包为views的返回函数)同时在几个goroutine上运行:

func makeHomeHandler() func(c *http.Conn, r *http.Request) {
    views := 1
    return func(c *http.Conn, r *http.Request) {
        fmt.Fprintf(c, "Counting %s, %d so far.", r.URL.Path[1:], views)
        views++
    }
}

看起来IO函数花了很长时间,结果我得到了这样的输出:

Counting monkeys, 5 so far.
Counting monkeys, 5 so far.
Counting monkeys, 5 so far.
Counting monkeys, 8 so far.
Counting monkeys, 8 so far.
Counting monkeys, 8 so far.
Counting monkeys, 11 so far.

它可以很好地递增,但是当它被打印出来时,我可以看到打印+递增操作根本不是原子的.

如果我将其更改为:

func makeHomeHandler() func(c *http.Conn, r *http.Request) {
    views := 0
    return func(c *http.Conn, r *http.Request) {
        views++
        // I can only hope that other goroutine does not increment the counter 
        // at this point, i.e., right after the previous line and before the 
        // next one are executed!
        views_now := views
        fmt.Fprintf(c, "Counting %s, %d so far.", r.URL.Path[1:], views_now)
    }
}

它似乎运行良好,但我不能完全肯定它最终是否不会失败……

推荐答案

如果同步计数器是您想要的全部,那么使用sync.Mutex是规范的解决方案.同步/原子包应该只用于低级别的东西,或者当您测量到严重的性能问题时.

type Counter struct {
    mu  sync.Mutex
    x   int64
}

func (c *Counter) Add(x int64) {
    c.mu.Lock()
    c.x += x
    c.mu.Unlock()
}

func (c *Counter) Value() (x int64) {
    c.mu.Lock()
    x = c.x
    c.mu.Unlock()
    return
}

func makeHomeHandler() func(c http.ResponseWriter, r *http.Request) {
    var views Counter
    return func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Counting %s, %d so far.", r.URL.Path[1:], views.Value())
        views.Add(1)
    }
}

对于您的特定问题,我建议定义一个满足http协议的新类型.处理程序接口,而不是返回闭包.这看起来也很简单:

type homeHandler struct {
    mu  sync.Mutex
    views   int64
}

func (h *homeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    h.mu.Lock()
    defer h.mu.Unlock()
    fmt.Fprintf(w, "Counting %s, %d so far.", r.URL.Path[1:], h.views)
    h.views++
}

func init() {
    http.Handle("/", new(homeHandler))
}

Go相关问答推荐

如何创建两个连接的io.ReadWriteClosers以用于测试目的

Go PQ驱动程序无法使用默认架构进行查询

GitHub发布Golang子模块

由docker中的nginx提供的样式和图像在页面上不起作用

埃拉托塞尼筛:加快交叉关闭倍数步骤

Golang在不写入磁盘的情况下为jpeg图像生成一致的哈希

通过 Terraform 中的 MapNestedAtribute 进行迭代

Golang 到 wasm 编译使用 tinygo.使用 wasmtime 执行

当图像是对象数组的元素时,如何显示存储为页面资源的图像?

go - 仅在函数即将返回错误时清理资源

grpc-gateway:重定向与定义不匹配(原始文件)

Dockerfile 问题 - 为什么找不到二进制 dlv - 没有这样的文件或目录

使用innerxml在 Go 中编码 XML 是否仅适用于某些类型?

如何 Select 前 N 个元素 Gin-Gorm

Go:如何创建一个可以提供配置文件中描述的 url 的服务器

如何在 Gorm 中获得特定日期的最大值?

如何使用 context.WithCancel 启动和停止每个会话的心跳?

如何断言类型是指向golang中接口的指针

如何迭代在泛型函数中传递的片的并集?

AWS EKS 上的 Golang REST API 部署因 CrashLoopBackOff 而失败