如何在高度并发系统中的golang中创建全局计数器

时间:2018-05-12 09:42:05

标签: go

我正在创建全局计数器,可以在goroutines之间共享。 参考此question,以下代码可能满足我的需求。

但是,如果存在大量并发请求,是否可能会将相同的数字分配给两个以上的goroutines? 如果是这样我怎么能避免这个?

谢谢!

补充评论)这个问题与我粘贴的链接不同,因为我想知道的是如何使用频道计数器避免重复。如果唯一可能的解决方案是sync.Mutex或atomic等其他实现,我将使用它。然而,根据链接(再次),频道似乎是最好的选择。任何评论或答案都非常有用。提前致谢。 我是多线程编码的新手,也可能是愚蠢的问题。对不起。

package main

import (
    "fmt"
    "time"
)

var counter int
var counter_chan chan int

func main() {
    counter_chan = make(chan int, 100)

    counter = 0

    go func() {
        for {
            select {
            case chanc := <-counter_chan:
                counter += chanc
                fmt.Printf("%d \n", counter)
            }
        }
    }()

    for i := 0; i < 10; i++ {
        go AddCounter(counter_chan)
    }

    time.Sleep(time.Second)
    fmt.Printf("Total Count is ... %d \n", GetCount())

}

func AddCounter(ch chan int) {
    ch <- 1
}

func GetCount() int {
    return counter
}

func ResetCount() {
    if counter > 8190 {
        counter = 0
    }
}

- 编辑2018年5月14日

假设以下代码对于获取和重置值是线程安全的。我是对的吗?

package main

import (
    "fmt"
    "time"
)

var counter int
var addCounterChan chan int
var readCounterChan chan int

func main() {
    addCounterChan = make(chan int, 100)
    readCounterChan = make(chan int, 100)

    counter = 0

    go func() {
        for {
            select {
            case val := <-addCounterChan:
                counter += val
                if counter > 5 {
                    counter = 0
                }
                readCounterChan <- counter
                fmt.Printf("%d \n", counter)
            }
        }
    }()

    for i := 0; i < 10; i++ {
        go AddCounter(addCounterChan)
    }

    time.Sleep(time.Second)

    for i := 0; i < 10; i++ {
        fmt.Printf("Total Count #%d is ... %d \n", (i + 1), GetCount(readCounterChan))
    }

}

// Following two functions will be implemented in another package in real case.
func AddCounter(ch chan int) {
    ch <- 1
}

func GetCount(ch chan int) int {
    r := <-ch
    return r
}

2 个答案:

答案 0 :(得分:6)

您问题的直接答案是:您已粘贴的代码安全地更新计数器,但无法安全地读取或重置。

与您链接的问题中的已接受答案相反,实现共享计数器的最简单,最有效的方法是使用atomic包。它可以用于原子地递增几种常见类型。例如:

function constTryCatch(valueFn, catchFn) {
  try {
    return valueFn();
  } catch (e) {
    if (catchFn) catchFn(e);
    return null;
  }
}

const obj = { foo: 'bar' };
const x = constTryCatch(() => obj.foo);
console.log(x);
const y = constTryCatch(() => obj.foo.bar.baz, (e) => console.log(e));
console.log(y);
// example, if the rest of the block depends on `y` being truthy:
// if (!y) return;

答案 1 :(得分:2)

使用sync.Mutex创建一个带有添加,获取和重置操作的计数器,如问题所示。

type counter struct {
    mu sync.Mutex
    n  int
}

func (c *counter) Add() {
    c.mu.Lock()
    c.n++
    c.mu.Unlock()
}

func (c *counter) Get() int {
    c.mu.Lock()
    n := c.n
    c.mu.Unlock()
    return n
}

func (c *counter) Reset() {
    c.mu.Lock()
    if c.n > 8190 {
        c.n = 0
    }
    c.mu.Unlock()
}

如果不需要重置功能,请使用sync/atomic

type counter struct {
    n int32
}

func (c *counter) Add() {
    atomic.AddInt32(&c.n, 1)
}

func (c *counter) Get() int {
    return int(atomic.LoadInt32(&c.n))
}