如何跨goroutines共享地图

如何跨goroutines共享地图,go,concurrency,synchronization,Go,Concurrency,Synchronization,我试图在Go中编写一个通知结构,它将保存一系列键及其各自的值,并且如果值低于阈值,将触发一个通知 当第一个样本低于阈值时,通知应仅触发一次,低于该阈值的其他样本不应再次触发,直到该值上升到阈值以上 例如,假设我的阈值是10,我发送了15、14、11、10。。。9发送9后,应触发通知。8、7、4的进一步样品不应造成任何影响。下面的示例(如5、6、7、9、10、11、14、30)应该不会对您造成任何影响。一旦样本再次低于10:30、20、15、10、7。。。必须发送另一个通知 当多个goroutin

我试图在Go中编写一个通知结构,它将保存一系列键及其各自的值,并且如果值低于阈值,将触发一个通知

当第一个样本低于阈值时,通知应仅触发一次,低于该阈值的其他样本不应再次触发,直到该值上升到阈值以上

例如,假设我的阈值是10,我发送了15、14、11、10。。。9发送9后,应触发通知。8、7、4的进一步样品不应造成任何影响。下面的示例(如5、6、7、9、10、11、14、30)应该不会对您造成任何影响。一旦样本再次低于10:30、20、15、10、7。。。必须发送另一个通知

当多个goroutine操作我的结构时,我遇到了一个问题

我尝试了使用sync.Mutex进行同步,也使用了sync.Map,但没有成功。我觉得有一个参考副本或缓存的地方,但我太新,去发现问题

为此,我创建了如下结构:

type Notifier interface {
    Send(message string)
}

type NotificationBoard struct {
    mutex    sync.Mutex
    Last     sync.Map
    notifier Notifier
}

func (n *NotificationBoard) Init(notifier Notifier) {
    n.notifier = notifier
}

// NotifyLess ...
func (n *NotificationBoard) NotifyLess(key string, value, threshold float64) {
    n.mutex.Lock()
    defer n.mutex.Unlock()

    if value >= threshold {
        fmt.Printf("NotificationBoard.NotifyLess %v (value >= threshold): %v >= %v\n", key, value, threshold)
        n.Last.Store(key, value)
        return
    }

    // value < threshold
    if last, found := n.Last.Load(key); found == true {
        fmt.Printf("NotificationBoard.NotifyLess %v (value < threshold): %v < %v : found %v\n", key, value, threshold, last)
        if last.(float64) >= threshold { // first trigger
            n.notifier.Send(fmt.Sprintf("%s < %v (%v)", key, threshold, value))
        }
    } else {
        fmt.Printf("NotificationBoard.NotifyLess %v (value < threshold): %v < %v : not found\n", key, value, threshold)
        // not found, started board as less
        n.notifier.Send(fmt.Sprintf("%s < %v (%v)", key, threshold, value))
    }

    n.Last.Store(key, value)
    return
}
为了简洁起见,我还添加了一些未包含在上面代码中的fmt.Println跟踪,并首先准备了一个单独的gouroutine测试,该测试按预期工作:

func Test1(t *testing.T) {
    board := newBoard()
    board.NotifyLess("k1", 15, 10)
    board.NotifyLess("k1", 10, 10)
    board.NotifyLess("k1", 5, 10)
    board.NotifyLess("k1", 4, 10)
    board.NotifyLess("k1", 3, 10)
    board.NotifyLess("k1", 10, 10)
    board.NotifyLess("k1", 15, 10)
    board.NotifyLess("k1", 20, 10)
    board.NotifyLess("k1", 15, 10)
    board.NotifyLess("k1", 10, 10)
    board.NotifyLess("k1", 5, 10)
    board.NotifyLess("k1", 1, 10)
}
产出:

> go test -run Test1
NotificationBoard.NotifyLess k1 (value >= threshold): 15 >= 10
NotificationBoard.NotifyLess k1 (value >= threshold): 10 >= 10
NotificationBoard.NotifyLess k1 (value < threshold): 5 < 10 : found 10
--------------> notifying k1 < 10 (5)
NotificationBoard.NotifyLess k1 (value < threshold): 4 < 10 : found 5
NotificationBoard.NotifyLess k1 (value < threshold): 3 < 10 : found 4
NotificationBoard.NotifyLess k1 (value >= threshold): 10 >= 10
NotificationBoard.NotifyLess k1 (value >= threshold): 15 >= 10
NotificationBoard.NotifyLess k1 (value >= threshold): 20 >= 10
NotificationBoard.NotifyLess k1 (value >= threshold): 15 >= 10
NotificationBoard.NotifyLess k1 (value >= threshold): 10 >= 10
NotificationBoard.NotifyLess k1 (value < threshold): 5 < 10 : found 10
--------------> notifying k1 < 10 (5)
NotificationBoard.NotifyLess k1 (value < threshold): 1 < 10 : found 5
PASS

任何想法?

这里简化逻辑的一种方法可能是运行一个修改映射的goroutine。然后,它可以侦听通道上的新值,因为如果按顺序处理值,它应该是正常的。你需要小心知道你的goroutine什么时候会回来,以确保它不会泄漏。通常,您不应该在goroutine之间共享数据,应该使用通道在goroutine之间进行通信。这是一个很好的频道介绍。是一个很好的解释成语,不要通过分享记忆来交流;相反,通过交流来共享内存

下面是一个示例,说明如何使用通道而不是共享内存来实现这种类型的应用程序


这里简化逻辑的一种方法可能是运行一个修改映射的goroutine。然后,它可以侦听通道上的新值,因为如果按顺序处理值,它应该是正常的。你需要小心知道你的goroutine什么时候会回来,以确保它不会泄漏。通常,您不应该在goroutine之间共享数据,应该使用通道在goroutine之间进行通信。这是一个很好的频道介绍。是一个很好的解释成语,不要通过分享记忆来交流;相反,通过交流来共享内存

下面是一个示例,说明如何使用通道而不是共享内存来实现这种类型的应用程序


我认为在设置这些跟踪器时需要设置标志,一个用于值上升时,另一个用于值下降时。我实现了一个

package main

import (
    "fmt"
    "sync"
)

const (
    threshold      int = 10
    upperThreshold int = 20
)

var mu sync.Mutex
var downwatch bool
var upwatch bool

func main() {
    var tracker int = 10
    var temp int = 1
    var sign int = 1
    for i := 1; i < 20; i++ {
        sign = sign * -1
        temp = temp + i
        go UpdateTracker(&tracker, temp*sign)
    }

    for {
    }
    return
}
func SetDownWatch() {
    downwatch = true
}
func SetUpWatch() {
    upwatch = true
}
func UnSetDownWatch() {
    downwatch = false
}
func UnSetUpWatch() {
    upwatch = false
}

func UpdateTracker(tracker *int, val int) {
    mu.Lock()
    defer mu.Unlock()
    if !(upwatch || downwatch) {
        if (*tracker)+val < threshold {
            NotifyOnDrop()
            SetDownWatch()
        }
        if (*tracker + val) > upperThreshold {
            NotifyOnRise()
            SetUpWatch()
        }

    }
    if (*tracker)+val < threshold && upwatch {
        NotifyOnDrop()
        SetDownWatch()
        UnSetUpWatch()
    }

    if (*tracker+val) > upperThreshold && downwatch {
        NotifyOnRise()
        SetUpWatch()
        UnSetDownWatch()
    }

    *tracker = (*tracker) + val
    fmt.Println((*tracker))
    return
}

func NotifyOnDrop() {
    fmt.Println("dropped")
    return
}

func NotifyOnRise() {
    fmt.Println("rose")
    return
}

updateTracker作为一个go例程运行,当值超过设置的阈值时打印到控制台。我认为这就是您所寻找的功能,这里缺少的是最后一个.Store函数,我相信它是您代码的自定义函数。我相信还有其他的处理方法。这个对我来说似乎很简单。

我认为在设置此类跟踪器时需要设置标志,一个用于值上升时,另一个用于值下降时。我实现了一个

package main

import (
    "fmt"
    "sync"
)

const (
    threshold      int = 10
    upperThreshold int = 20
)

var mu sync.Mutex
var downwatch bool
var upwatch bool

func main() {
    var tracker int = 10
    var temp int = 1
    var sign int = 1
    for i := 1; i < 20; i++ {
        sign = sign * -1
        temp = temp + i
        go UpdateTracker(&tracker, temp*sign)
    }

    for {
    }
    return
}
func SetDownWatch() {
    downwatch = true
}
func SetUpWatch() {
    upwatch = true
}
func UnSetDownWatch() {
    downwatch = false
}
func UnSetUpWatch() {
    upwatch = false
}

func UpdateTracker(tracker *int, val int) {
    mu.Lock()
    defer mu.Unlock()
    if !(upwatch || downwatch) {
        if (*tracker)+val < threshold {
            NotifyOnDrop()
            SetDownWatch()
        }
        if (*tracker + val) > upperThreshold {
            NotifyOnRise()
            SetUpWatch()
        }

    }
    if (*tracker)+val < threshold && upwatch {
        NotifyOnDrop()
        SetDownWatch()
        UnSetUpWatch()
    }

    if (*tracker+val) > upperThreshold && downwatch {
        NotifyOnRise()
        SetUpWatch()
        UnSetDownWatch()
    }

    *tracker = (*tracker) + val
    fmt.Println((*tracker))
    return
}

func NotifyOnDrop() {
    fmt.Println("dropped")
    return
}

func NotifyOnRise() {
    fmt.Println("rose")
    return
}

updateTracker作为一个go例程运行,当值超过设置的阈值时打印到控制台。我认为这就是您所寻找的功能,这里缺少的是最后一个.Store函数,我相信它是您代码的自定义函数。我相信还有其他的处理方法。这个对我来说似乎很简单。

有很多代码需要阅读和理解。我建议使用race检测器运行应用程序并报告。了解代码运行时是否没有竞争,或者代码所在的位置是否存在竞争,这将很有帮助。测试刚刚通过我看不出你的输出有问题。。。除非我只是有点瞎。您发布的输出似乎只是碰巧按顺序运行的goroutines。哪个特定的输出显示失败?有很多代码需要阅读和理解。我建议使用race检测器运行应用程序并报告。了解代码运行时是否没有竞争,或者代码所在的位置是否存在竞争,这将很有帮助。测试刚刚通过我看不出你的输出有问题。。。除非我只是有点瞎。您发布的输出似乎只是碰巧按顺序运行的goroutines。哪个特定输出显示故障?已修复!另外,我注意到我最初的测试有点不公平。我毫不拖延地开始了许多goroutine,因此,有时,一些例程远远落后于其他例程,发送的样本高于阈值。我的意思是,在我的测试中,我的样本并没有一直单调地变小,从而导致了嘈杂的日志。任何
另外,我将我的实现更改为使用通道。谢谢:谢谢!另外,我注意到我最初的测试有点不公平。我毫不拖延地开始了许多goroutine,因此,有时,一些例程远远落后于其他例程,发送的样本高于阈值。我的意思是,在我的测试中,我的样本并没有一直单调地变小,从而导致了嘈杂的日志。无论如何,我将实现更改为使用通道。谢谢:D
> go test -run Test3
routine 7 waiting preparation...
routine 2 waiting preparation...
routine 2 notifying 15
NotificationBoard.NotifyLess keyX (value >= threshold): 15.03 >= 10
routine 2 notifying 14
NotificationBoard.NotifyLess keyX (value >= threshold): 14.03 >= 10
routine 2 notifying 13
NotificationBoard.NotifyLess keyX (value >= threshold): 13.03 >= 10
routine 2 notifying 12
NotificationBoard.NotifyLess keyX (value >= threshold): 12.03 >= 10
routine 2 notifying 11
NotificationBoard.NotifyLess keyX (value >= threshold): 11.03 >= 10
routine 2 notifying 10
NotificationBoard.NotifyLess keyX (value >= threshold): 10.03 >= 10
routine 2 notifying 9
NotificationBoard.NotifyLess keyX (value < threshold): 9.03 < 10 : found 10.03
--------------> notifying keyX < 10 (9.03)
routine 2 notifying 8
NotificationBoard.NotifyLess keyX (value < threshold): 8.03 < 10 : found 9.03
routine 2 notifying 7
NotificationBoard.NotifyLess keyX (value < threshold): 7.03 < 10 : found 8.03
routine 2 notifying 6
NotificationBoard.NotifyLess keyX (value < threshold): 6.03 < 10 : found 7.03
routine 14 waiting preparation...
routine 14 notifying 15
NotificationBoard.NotifyLess keyX (value >= threshold): 15.15 >= 10
routine 14 notifying 14
NotificationBoard.NotifyLess keyX (value >= threshold): 14.15 >= 10
routine 14 notifying 13
NotificationBoard.NotifyLess keyX (value >= threshold): 13.15 >= 10
routine 14 notifying 12
NotificationBoard.NotifyLess keyX (value >= threshold): 12.15 >= 10
routine 14 notifying 11
NotificationBoard.NotifyLess keyX (value >= threshold): 11.15 >= 10
routine 14 notifying 10
NotificationBoard.NotifyLess keyX (value >= threshold): 10.15 >= 10
routine 14 notifying 9
NotificationBoard.NotifyLess keyX (value < threshold): 9.15 < 10 : found 10.15
--------------> notifying keyX < 10 (9.15)
routine 14 notifying 8
NotificationBoard.NotifyLess keyX (value < threshold): 8.15 < 10 : found 9.15
routine 14 notifying 7
NotificationBoard.NotifyLess keyX (value < threshold): 7.15 < 10 : found 8.15
routine 14 notifying 6
NotificationBoard.NotifyLess keyX (value < threshold): 6.15 < 10 : found 7.15
routine 22 waiting preparation...
routine 27 waiting preparation...
routine 27 notifying 15
NotificationBoard.NotifyLess keyX (value >= threshold): 15.28 >= 10
routine 27 notifying 14
NotificationBoard.NotifyLess keyX (value >= threshold): 14.28 >= 10
routine 27 notifying 13
NotificationBoard.NotifyLess keyX (value >= threshold): 13.28 >= 10
routine 27 notifying 12
NotificationBoard.NotifyLess keyX (value >= threshold): 12.28 >= 10
routine 27 notifying 11
NotificationBoard.NotifyLess keyX (value >= threshold): 11.28 >= 10
routine 27 notifying 10
NotificationBoard.NotifyLess keyX (value >= threshold): 10.28 >= 10
routine 27 notifying 9
NotificationBoard.NotifyLess keyX (value < threshold): 9.28 < 10 : found 10.28
--------------> notifying keyX < 10 (9.28)
routine 27 notifying 8
NotificationBoard.NotifyLess keyX (value < threshold): 8.28 < 10 : found 9.28
routine 27 notifying 7
NotificationBoard.NotifyLess keyX (value < threshold): 7.28 < 10 : found 8.28
routine 27 notifying 6
NotificationBoard.NotifyLess keyX (value < threshold): 6.28 < 10 : found 7.28
routine 20 waiting preparation...
routine 20 notifying 15
NotificationBoard.NotifyLess keyX (value >= threshold): 15.21 >= 10
routine 20 notifying 14
NotificationBoard.NotifyLess keyX (value >= threshold): 14.21 >= 10
routine 20 notifying 13
NotificationBoard.NotifyLess keyX (value >= threshold): 13.21 >= 10
routine 20 notifying 12
NotificationBoard.NotifyLess keyX (value >= threshold): 12.21 >= 10
routine 20 notifying 11
NotificationBoard.NotifyLess keyX (value >= threshold): 11.21 >= 10
routine 20 notifying 10
NotificationBoard.NotifyLess keyX (value >= threshold): 10.21 >= 10
routine 20 notifying 9
NotificationBoard.NotifyLess keyX (value < threshold): 9.21 < 10 : found 10.21
--------------> notifying keyX < 10 (9.21)
routine 20 notifying 8
NotificationBoard.NotifyLess keyX (value < threshold): 8.21 < 10 : found 9.21
routine 20 notifying 7
NotificationBoard.NotifyLess keyX (value < threshold): 7.21 < 10 : found 8.21
routine 20 notifying 6
NotificationBoard.NotifyLess keyX (value < threshold): 6.21 < 10 : found 7.21
routine 19 waiting preparation...
routine 19 notifying 15
NotificationBoard.NotifyLess keyX (value >= threshold): 15.2 >= 10
routine 19 notifying 14
NotificationBoard.NotifyLess keyX (value >= threshold): 14.2 >= 10
routine 19 notifying 13
NotificationBoard.NotifyLess keyX (value >= threshold): 13.2 >= 10
routine 19 notifying 12
NotificationBoard.NotifyLess keyX (value >= threshold): 12.2 >= 10
routine 19 notifying 11
NotificationBoard.NotifyLess keyX (value >= threshold): 11.2 >= 10
routine 19 notifying 10
NotificationBoard.NotifyLess keyX (value >= threshold): 10.2 >= 10
routine 19 notifying 9
NotificationBoard.NotifyLess keyX (value < threshold): 9.2 < 10 : found 10.2
--------------> notifying keyX < 10 (9.2)
routine 19 notifying 8
NotificationBoard.NotifyLess keyX (value < threshold): 8.2 < 10 : found 9.2
routine 19 notifying 7
NotificationBoard.NotifyLess keyX (value < threshold): 7.2 < 10 : found 8.2
routine 19 notifying 6
NotificationBoard.NotifyLess keyX (value < threshold): 6.2 < 10 : found 7.2
routine 0 waiting preparation...
routine 0 notifying 15
NotificationBoard.NotifyLess keyX (value >= threshold): 15.01 >= 10
routine 0 notifying 14
NotificationBoard.NotifyLess keyX (value >= threshold): 14.01 >= 10
routine 0 notifying 13
NotificationBoard.NotifyLess keyX (value >= threshold): 13.01 >= 10
routine 0 notifying 12
NotificationBoard.NotifyLess keyX (value >= threshold): 12.01 >= 10
routine 0 notifying 11
NotificationBoard.NotifyLess keyX (value >= threshold): 11.01 >= 10
routine 0 notifying 10
NotificationBoard.NotifyLess keyX (value >= threshold): 10.01 >= 10
routine 0 notifying 9
NotificationBoard.NotifyLess keyX (value < threshold): 9.01 < 10 : found 10.01
--------------> notifying keyX < 10 (9.01)
routine 0 notifying 8
NotificationBoard.NotifyLess keyX (value < threshold): 8.01 < 10 : found 9.01
routine 0 notifying 7
NotificationBoard.NotifyLess keyX (value < threshold): 7.01 < 10 : found 8.01
routine 0 notifying 6
NotificationBoard.NotifyLess keyX (value < threshold): 6.01 < 10 : found 7.01
routine 17 waiting preparation...
routine 17 notifying 15
NotificationBoard.NotifyLess keyX (value >= threshold): 15.18 >= 10
routine 17 notifying 14
NotificationBoard.NotifyLess keyX (value >= threshold): 14.18 >= 10
routine 17 notifying 13
NotificationBoard.NotifyLess keyX (value >= threshold): 13.18 >= 10
routine 17 notifying 12
NotificationBoard.NotifyLess keyX (value >= threshold): 12.18 >= 10
routine 17 notifying 11
NotificationBoard.NotifyLess keyX (value >= threshold): 11.18 >= 10
routine 17 notifying 10
NotificationBoard.NotifyLess keyX (value >= threshold): 10.18 >= 10
routine 17 notifying 9
NotificationBoard.NotifyLess keyX (value < threshold): 9.18 < 10 : found 10.18
--------------> notifying keyX < 10 (9.18)
routine 17 notifying 8
NotificationBoard.NotifyLess keyX (value < threshold): 8.18 < 10 : found 9.18
routine 17 notifying 7
NotificationBoard.NotifyLess keyX (value < threshold): 7.18 < 10 : found 8.18
routine 17 notifying 6
NotificationBoard.NotifyLess keyX (value < threshold): 6.18 < 10 : found 7.18
routine 15 waiting preparation...
routine 16 waiting preparation...
...... continues
...
NotificationBoard.NotifyLess keyX (value >= threshold): 10.22 >= 10
routine 21 notifying 9
NotificationBoard.NotifyLess keyX (value >= threshold): 10.07 >= 10
routine 6 notifying 9
NotificationBoard.NotifyLess keyX (value < threshold): 9.08 < 10 : found 10.07
--------------> notifying keyX < 10 (9.08)
routine 7 notifying 8
NotificationBoard.NotifyLess keyX (value < threshold): 9.17 < 10 : found 9.08
routine 16 notifying 8
NotificationBoard.NotifyLess keyX (value >= threshold): 10.11 >= 10
routine 10 notifying 9
NotificationBoard.NotifyLess keyX (value < threshold): 9.3 < 10 : found 10.11
--------------> notifying keyX < 10 (9.3)
routine 29 notifying 8
NotificationBoard.NotifyLess keyX (value < threshold): 9.19 < 10 : found 9.3
routine 18 notifying 8
...
> go version
go version go1.10.2 windows/amd64
package main

import (
    "fmt"
    "sync"
)

type value struct {
    key       string
    value     float64
    threshold float64
}

func main() {
    b := board{
        last: map[string]float64{},
    }
    c := b.start()
    wg := sync.WaitGroup{}
    for i := 0; i < 30; i++ {
        wg.Add(1)
        go func(i int) {
            for j := 15.0; j > 5; j-- {
                c <- value{"k1", j + float64(i+1)/100, 10}
            }
            wg.Done()
        }(i)
    }
    wg.Wait()
    close(c)
}

type board struct {
    last map[string]float64
}

func (b *board) start() chan<- value {
    c := make(chan value)
    go func() {
        for v := range c {
            b.notify(v)
        }
    }()
    return c
}

func (b *board) notify(v value) {
    if l, ok := b.last[v.key]; !ok || l >= v.threshold {
        if v.value < v.threshold {
            fmt.Printf("%s < %v (%v)\n", v.key, v.threshold, v.value)
        }
    }
    b.last[v.key] = v.value
}
package main

import (
    "fmt"
    "sync"
)

const (
    threshold      int = 10
    upperThreshold int = 20
)

var mu sync.Mutex
var downwatch bool
var upwatch bool

func main() {
    var tracker int = 10
    var temp int = 1
    var sign int = 1
    for i := 1; i < 20; i++ {
        sign = sign * -1
        temp = temp + i
        go UpdateTracker(&tracker, temp*sign)
    }

    for {
    }
    return
}
func SetDownWatch() {
    downwatch = true
}
func SetUpWatch() {
    upwatch = true
}
func UnSetDownWatch() {
    downwatch = false
}
func UnSetUpWatch() {
    upwatch = false
}

func UpdateTracker(tracker *int, val int) {
    mu.Lock()
    defer mu.Unlock()
    if !(upwatch || downwatch) {
        if (*tracker)+val < threshold {
            NotifyOnDrop()
            SetDownWatch()
        }
        if (*tracker + val) > upperThreshold {
            NotifyOnRise()
            SetUpWatch()
        }

    }
    if (*tracker)+val < threshold && upwatch {
        NotifyOnDrop()
        SetDownWatch()
        UnSetUpWatch()
    }

    if (*tracker+val) > upperThreshold && downwatch {
        NotifyOnRise()
        SetUpWatch()
        UnSetDownWatch()
    }

    *tracker = (*tracker) + val
    fmt.Println((*tracker))
    return
}

func NotifyOnDrop() {
    fmt.Println("dropped")
    return
}

func NotifyOnRise() {
    fmt.Println("rose")
    return
}