gpt4 book ai didi

multithreading - Postgres 锁在不同的 goroutine 上表现不一致

转载 作者:IT王子 更新时间:2023-10-29 00:47:50 25 4
gpt4 key购买 nike

我在 go 例程和 PostgreSQL 9.5 中遇到了一些与 pg_locks 不一致的行为。

当我创建额外的 goroutines 并调用 SELECT pg_try_advisory_lock(1); 时,结果不一致。

我可以在某个时刻尝试获取锁,失败,重试并设法获取它,同时没有任何人显式释放锁。

我创建了一个小程序来重现该问题。

程序流程

  1. 创建 10 个协程。他们每个人都试图在初始化时获得相同的锁。
  2. 每一秒,每个实例都会尝试再次获取锁,如果它还没有获取的话。
  3. 每一秒我都会探测所有实例并计算有多少已经获得了锁。

预期行为:

在任何给定时刻,只有 1 个 goroutine 会拥有锁。

实际结果:

设法获取锁的 goroutines 的数量随着时间的推移而增加。


package main

var dbMap *gorp.DbMap // init code omitted for brevity

func main() {
setup(10)
fmt.Println("after initialization,", countOwners(), "instances of lockOwners have the lock!")

for {
if _, err := dbMap.Exec("SELECT pg_sleep(1)"); err != nil {
panic(err)
}

fmt.Println(countOwners(), "instances of lockOwners have the lock!")
}
}

func countOwners() int {
possessLock := 0
for _, lo := range los {
if lo.hasLock {
possessLock++
}
}
return possessLock
}

var los []*lockOwner

func setup(instanceCount int) {
var wg sync.WaitGroup
for i := 0; i < instanceCount; i++ {
wg.Add(1)
newInstance := lockOwner{}
los = append(los, &newInstance)
go newInstance.begin(time.Second, &wg, i+1)
}
wg.Wait()
}

type lockOwner struct {
id int
ticker *time.Ticker
hasLock bool
}

func (lo *lockOwner) begin(interval time.Duration, wg *sync.WaitGroup, id int) {
lo.ticker = time.NewTicker(interval)
lo.id = id
go func() {
lo.tryToGetLock()
wg.Done()
for range lo.ticker.C {
lo.tryToGetLock()
}
}()
}

func (lo *lockOwner) tryToGetLock() {

if lo.hasLock {
return
}

locked, err := dbMap.SelectStr("SELECT pg_try_advisory_lock(4);")
if err != nil {
panic(err)
}

if locked == "true" {
fmt.Println(lo.id, "Did get lock!")
lo.hasLock = true
}
}

这个程序的输出各不相同,但通常是这样的:

1 Did get lock!
after initialization, 1 instances of lockOwners have the lock!
1 instances of lockOwners have the lock!
2 Did get lock!
2 instances of lockOwners have the lock!
2 instances of lockOwners have the lock!
7 Did get lock!
3 instances of lockOwners have the lock!
3 instances of lockOwners have the lock!
6 Did get lock!
4 instances of lockOwners have the lock!

我的问题:

  1. 以这种方式使用 pg_locks 时,我应该期望保护什么?
  2. 某个goroutine获取锁失败的原因是什么?
  3. 同一个 goroutine 在下一次尝试时成功的原因是什么?

    • 会不会是线程是被锁定的资源,每次 goroutine 触发它都是从不同的线程执行的?这可以解释不一致的行为。

最佳答案

在通过 gorp 使用 PostgreSQL 几个月后,我想我理解了这种行为:

  • gorp 维护一个连接池。
  • 每当我们创建交易时,这些连接之一将被随机选择(?)。
  • dbMap.SomeCommand() 创建一个事务,执行一些命令并提交事务。
  • pg_try_advisory_lock 适用于 session level .
  • A session is synonymous with a TCP connection ,因此绝不是稳定或永久的。
  • 池中的连接尽可能保持使用相同的 session ,但会在需要时重置它。

每当我们执行 dbMap.SelectStr("SELECT pg_try_advisory_lock(4);") 时,都会从池中选择一个连接。然后创建一个事务,它在 session 级别获取锁,然后提交。

当另一个 go-routine 尝试做同样的事情时,它有可能会使用相同的 session ,这可能取决于从池中获取的连接。由于锁定是在 session 级别完成的 - 新事务能够再次获取锁。

关于multithreading - Postgres 锁在不同的 goroutine 上表现不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52168321/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com