繁体   English   中英

GoLang 顺序 goroutine

[英]GoLang sequential goroutines

我是 golang 的新手,并且有一个用例,其中对类型的值的操作必须以顺序方式运行,而对其他类型的值的操作可以同时运行。

  1. 想象一下数据来自流连接(按顺序)
     key_name_1, value_1 key_name_2, value_2 key_name_1, value_1
  2. 现在, key_name_1key_name_2可以由 goroutine 并发操作。
  3. 但是由于下一个流式值(第 3 行)再次是key_name_1 ,所以如果前面的操作(第 1 行)已经完成,这个操作应该只由 goroutine 处理,否则它应该等待第一个操作完成才能应用操作。 为了便于讨论,我们可以假设操作只是将新值添加到先前值。

在golang中以最高性能实现这一目标的正确方法是什么?


确切的用例是数据库更改在队列上进行流式传输,现在如果一个值发生更改,重要的是在另一个数据库上对该操作应用相同的序列,否则一致性将受到影响。 冲突很少见,但可能会发生。

作为给定密钥的互斥性的简单解决方案,您可以只使用一个锁定的 map 的引用计数锁。 它不是高负载的最佳选择,但在您的情况下可能就足够了。

type processLock struct {
    mtx      sync.Mutex
    refcount int
}

type locker struct {
    mtx   sync.Mutex
    locks map[string]*processLock
}

func (l *locker) acquire(key string) {
    l.mtx.Lock()
    lk, found := l.locks[key]
    if !found {
        lk = &processLock{}
        l.locks[key] = lk
    }
    lk.refcount++
    l.mtx.Unlock()
    lk.mtx.Lock()
}

func (l *locker) release(key string) {
    l.mtx.Lock()
    lk := l.locks[key]
    lk.refcount--
    if lk.refcount == 0 {
        delete(l.locks, key)
    }
    l.mtx.Unlock()
    lk.mtx.Unlock()
}

只需在处理密钥之前调用acquire(key) ,完成后调用release(key)

现场演示

警告,上面的代码保证排他性。 但不是序列。 要按顺序解锁,您需要一个FIFO mutex

只是关于具有固定数量的工人的消息路由的建议。

IDK 如果它是最快的,我猜不是,但它很简单且可重新配置(通过一些努力可以使其 JiT 可调整大小)。

我确实相信散列部分可以优化,并且存在一些聪明的技术。 否则,您还可以通过增加通道缓冲区来堆积 memory 中的一些项目,再次简单。

无论如何, https://go.dev/play/p/fnxNJ9VZK8q

// You can edit this code!
// Click here and start typing.
package main

import (
    "fmt"
    "hash/fnv"
    "sync"
)

func main() {
    events := []map[string]interface{}{
        map[string]interface{}{
            "key1": "v",
            "key2": "v",
            "key3": "v",
        },
        map[string]interface{}{
            "key1": "v",
            "key2": "v",
            "key3": "v",
        },
    }

    type workerWork struct {
        work     interface{}
        workerID int
    }

    workers := 4
    inputs := []chan interface{}{}
    output := make(chan workerWork)

    var wg sync.WaitGroup
    for i := 0; i < workers; i++ {
        i := i
        input := make(chan interface{})
        inputs = append(inputs, input)
        wg.Add(1)
        go func() {
            defer wg.Done()
            for v := range input {
                output <- workerWork{work: v, workerID: i}
            }
        }()
    }

    go func() {
        wg.Wait()
        close(output)
    }()

    go func() {
        h := fnv.New32a()
        for _, batch := range events {
            for k, v := range batch {
                h.Write([]byte(k))
                ui := int(h.Sum32())
                d := ui % workers
                inputs[d] <- map[string]interface{}{k: v}
                h.Reset()
            }
        }
        for _, i := range inputs {
            close(i)
        }
    }()

    for work := range output {
        fmt.Printf("%#v\n", work)
    }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM