英文:
GoLang sequential goroutines
问题
我是新手学习golang,有一个使用场景,其中对一种类型的值的操作必须按顺序运行,而对另一种类型的值的操作可以并发运行。
- 假设数据是从流连接中传输的(有序的):
key_name_1, value_1 key_name_2, value_2 key_name_1, value_1
- 现在,
key_name_1
和key_name_2
可以由 goroutine 并发操作。 - 但是,由于下一个流式传输的值(第三行)仍然是
key_name_1
,所以只有在之前的操作(第一行)完成后,才能由 goroutine 处理这个操作。否则,它应该等待第一次操作完成后再应用该操作。对于讨论的目的,我们可以假设操作只是将新值添加到先前的值中。
在golang中,如何以最高的性能实现这一点?
确切的使用场景是数据库更改以队列的形式进行流式传输,现在如果一个值正在发生变化,重要的是在另一个数据库上按照相同的顺序应用该操作,否则一致性将受到影响。冲突很少发生,但可能会发生。
英文:
I am new to golang and have a use case where operations on a value of a type have to run in a sequential manner where as operation on value of other type can be run concurrently.
- Imagine data is coming from a streaming connection (In-order)
key_name_1, value_1 key_name_2, value_2 key_name_1, value_1
- Now,
key_name_1
, andkey_name_2
can be operated by goroutine concurrently. - But as next streamed value (3rd row) is
key_name_1
again, so this operation should only be processed by goroutine if the earlier operation (1st row) has finished otherwise it should wait for the 1st operation to finish before it can apply the operation.
For the sake of discussion we can assume that operation is simply
adding the new value to previous value.
What would be the right way to achieve this in golang with highest possible performance ?
The exact use case is database changes are streamed on a queue, now if a value is getting changed its important that onto another database that operation is applied on the same sequence otherwise consistency will get impacted. Conflicts are rare, but can happen.
答案1
得分: 1
作为给定键的互斥性的简单解决方案,您可以使用一个带有引用计数锁的锁定映射。对于高负载来说可能不是最优的,但在您的情况下可能足够。
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互斥锁。
英文:
As a simple solution for mutual exclusivity for a given key you can just use a locked map of ref-counted locks. It's not the most optimal for high loads, but might just suffice in your case.
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()
}
Just call acquire(key)
before processing a key and release(key)
when done with it.
Warning! The code above guarantees exclusivity, but not sequence. To sequentialize the unlocking you need a FIFO mutex.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论