Как поделиться картой через рутины - PullRequest
0 голосов
/ 13 сентября 2018

Я пытаюсь закодировать структуру уведомлений в 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 ... необходимо отправить другое уведомление.

У меня проблема, когда несколько структур управляют моей структурой.

Я попытался синхронизировать с sync.Mutex, а также использовать sync.Map, но не повезло. Я чувствую, что где-то есть эталонная копия или кеширование, но я слишком новичок в Go, чтобы найти проблему.

для этого я создал такую ​​структуру:

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
}

Я знаю, что этого должно быть достаточно, используя sync.Mutex ИЛИ sync.Map, но код выше имеет и то и другое, потому что это моя текущая (неработающая) версия.

для тестирования я настроил следующий код:

type dummy struct{}

func (d *dummy) Send(message string) {
    fmt.Println("--------------> notifying", message)
}

func newBoard() *NotificationBoard {
    notificationBoard := &NotificationBoard{}
    notificationBoard.Init(&dummy{})
    return notificationBoard
}

Я также добавил несколько следов fmt.Println (для краткости не включенных в мой код выше) и подготовил сначала тест для одного гурутина (который работает как положено):

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)
}
* * Тысяча двадцать-одина выходы: * * 1 022
> 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

мы видим, что вывод «уведомлять ....» происходит дважды, только в моменты, когда выборка падает ниже порога

но затем я создал тест для нескольких гуру, а затем уведомления появляются несколько раз:

func Test3(t *testing.T) {
    preparing := sync.WaitGroup{}
    preparing.Add(1)

    board := newBoard()
    wg := sync.WaitGroup{}

    for i := 0; i < 30; i++ {
        wg.Add(1)
        go func(x int, not *NotificationBoard) {
            fmt.Printf("routine %v waiting preparation... \n", x)
            preparing.Wait()

            for j := 15.0; j > 5; j-- {
                fmt.Printf("routine %v notifying %v\n", x, j)
                not.NotifyLess("keyX", j+float64(x+1)/100, 10)
            }

            wg.Done()
        }(i, board)
    }

    preparing.Done()
    wg.Wait()

}

который выводит:

> 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

Я добавил десятичное значение для представления goroutine, и, глядя на вывод, казалось, что каждая goroutine имеет свою собственную копию карты, так как они находили предыдущее значение с теми же десятичными разрядами. но потом я нашел:

...
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

какие-нибудь идеи?

Ответы [ 2 ]

0 голосов
/ 13 сентября 2018

Я думаю, вам нужно установить флаги, когда вы устанавливаете такие трекеры, один для того, когда значение увеличивается, и другой, когда значение уменьшается.Я реализовал один

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 и выводит на консоль, когда значение пересекает установленный порог.Я думаю, что это именно та функциональность, которую вы искали, здесь не хватает только функции Last.Store, которая, как мне кажется, предназначена для вашего кода.Я уверен, что есть другие способы справиться с этим.Этот показался мне достаточно простым.

0 голосов
/ 13 сентября 2018

Один из способов упростить логику здесь может состоять в запуске единственной программы, которая модифицирует карту.Затем он может прослушивать новые значения в канале (поскольку все должно быть в порядке, если значения обрабатываются последовательно).Вы должны быть осторожны, чтобы знать, когда ваша программа вернется, чтобы убедиться, что она не протекает.В общем, вы не должны делиться данными между программами, вы должны использовать каналы, чтобы общаться между программами.https://gobyexample.com/channels - хорошее введение в каналы.https://blog.golang.org/share-memory-by-communicating является хорошим объяснением идиомы: «Не связывайтесь, разделяя память; вместо этого делитесь памятью, общаясь».

Вот пример того, как вы могли бы реализовать приложение такого типа, используяканалы, вместо того, чтобы делиться памятью ( версия игровой площадки ).

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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...