gpt4 book ai didi

multithreading - 为什么基于 channel 的 Lock block ?

转载 作者:数据小太阳 更新时间:2023-10-29 03:29:52 30 4
gpt4 key购买 nike

嗨,我正在写一个 Lock使用 channel ,旨在锁定/解锁给定“应用程序”的操作。

总体思路是,一个协程持续监听两个 channel :lockChunlockCh .任何Lock()操作发送自制 channel 到lockCh , 并等待从那个自制 channel 读取,从读取这个 channel 完成意味着 Lock()成功。

类似的过程适用于 Unlock() .

对于监听器 gorouting,它会在接受 Lock() 时检查“应用程序”是否已被锁定,如果是这样,它将把那个自制的 channel 放到等待列表的尾部。如果有人Unlock() ,它会唤醒(通过向 channel 发送消息)下一个服务员,或者如果没有其他人在等待锁,则删除服务员列表。

代码在下面,我不知道哪里错了,但是测试用例就是不通过(它在几个Lock()Unlock()之后阻塞!)

谢谢你给我一些建议。

type receiver struct {
app string
ch chan struct{}
next *receiver
}

type receiveList struct {
head *receiver
tail *receiver
}

type AppLock struct {
lockCh chan receiver
unlockCh chan receiver

// Consider lock x:
// if map[x] doesn't exist, x is unlocked
// if map[x] exist but head is nil, x is locked but no waiter
// if map[x] exist and head isn't nil, x is locked and there're waiters
m map[string]receiveList
}

func NewAppLock() *AppLock {
l := new(AppLock)
l.lockCh = make(chan receiver)
l.unlockCh = make(chan receiver)
l.m = make(map[string]receiveList)

go l.lockRoutine()
return l
}

func (l *AppLock) Lock(app string) {
ch := make(chan struct{})
l.lockCh <- receiver{
app: app,
ch: ch,
}
<-ch
}

func (l *AppLock) Unlock(app string) {
ch := make(chan struct{})
l.unlockCh <- receiver{
app: app,
ch: ch,
}
<-ch
}

func (l *AppLock) lockRoutine() {
for {
select {
case r := <-l.lockCh:
rlist, ok := l.m[r.app]
if ok { // already locked
if rlist.head == nil { // no waiter
rlist.head = &r
rlist.tail = &r
} else { // there're waiters, wait in queue
rlist.tail.next = &r
rlist.tail = &r
}
} else { // unlocked
rlist = receiveList{}
l.m[r.app] = rlist
r.ch <- struct{}{}
}
case r := <-l.unlockCh:
rlist, ok := l.m[r.app]
if ok {
if rlist.head == nil { // no waiter
delete(l.m, r.app)
r.ch <- struct{}{}
} else { // there're waiters
candidate := rlist.head
if rlist.head == rlist.tail {
rlist.head = nil
rlist.tail = nil
} else {
rlist.head = rlist.head.next
}
candidate.ch <- struct{}{}
r.ch <- struct{}{}
}
} else {
panic("AppLock: inconsistent lock state")
}
}
}
}

测试:

func main() {
app := "APP"
appLock := NewAppLock()
c := make(chan bool)

for i := 0; i < 10; i++ {
go func(l *AppLock, loops int, cdone chan bool) {
for i := 0; i < loops; i++ {
l.Lock(app)
l.Unlock(app)
}
cdone <- true
}(appLock, 1, c)
}

for i := 0; i < 10; i++ {
<-c
}

fmt.Println("DONE")
}

最佳答案

我刚刚在我的代码中发现了错误。

type AppLock struct {
lockCh chan receiver
unlockCh chan receiver

// Consider lock x:
// if map[x] doesn't exist, x is unlocked
// if map[x] exist but head is nil, x is locked but no waiter
// if map[x] exist and head isn't nil, x is locked and there're waiters
m map[string]receiveList
}

最初,我认为 receiveList 中的 headtail 都是指针,所以我们总是可以对同一个 waiters-list 进行操作,即使receiveList 不是指针类型。 (显然是错的)

的确,如果我们只从headtail 读取数据,而不使用receiveList 的Pointer 类型也是可以的。但是,我确实在 lockRoutine 中写入它们,它正在写入它们的副本。这就是问题所在。

关于multithreading - 为什么基于 channel 的 Lock block ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39332596/

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