英文:
Is it a good practice to lock some data outside if-else and unlock inside if-else
问题
当我测试rf.state == candidate
时,可能会出现竞争条件。所以在此之前,我会锁定结构体rf
。我想确保rf.state
在if
条件测试中出现的时候,我可以使用它的最新值。
有没有其他习惯用法来在类似情况下锁定数据(if-else、switch...)?
编辑:
这是我其他一些代码。
...
rf.mu.Lock()
switch rf.state {
case follower:
rf.mu.Unlock()
select {
case <-rf.chanAppends:
case <-rf.chanGrantVotes:
case <-time.After(time.Duration(500+rand.Intn(1000)) * time.Millisecond):
rf.mu.Lock()
rf.state = candidate
rf.mu.Unlock()
}
case candidate:
rf.mu.Unlock()
...
case leader:
rf.mu.Unlock()
...
}
为了在switch
中使用当前的rf.state
,我必须在外部进行锁定,并在内部进行解锁。让我困扰的是,对于每个case
,我都必须解锁(使代码变得不够清晰和可读?)。而且在每个case
中都有一些阻塞操作(例如time.After(...)
),所以我看不到使用defer
来帮助我的方法。我想这是一种权衡,如果我不想改变这个switch
的逻辑结构的话。
英文:
When I test rf.state == candidate
, there could be a race. So I lock the struct rf
before that. I want to make sure rf.state
appears exactly in the if
condition test so I can use its most current value.
...
func (rf *Raft) runElection() {
rf.mu.Lock()
rf.currentTerm += 1
rf.votedFor = rf.me
rf.votes = 1
rf.mu.Unlock()
for server := range rf.peers {
rf.mu.Lock()
if server != rf.me && rf.state == candidate {
rf.mu.Unlock()
go rf.sendRequestVote(server)
} else {
rf.mu.Unlock()
}
}
}
...
Is there any other idiomatic way to lock the data in similar situations (if-else, switch...)?
EDITS:
Here is some other code of mine.
...
rf.mu.Lock()
switch rf.state {
case follower:
rf.mu.Unlock()
select {
case <-rf.chanAppends:
case <-rf.chanGrantVotes:
case <-time.After(time.Duration(500+rand.Intn(1000)) * time.Millisecond):
rf.mu.Lock()
rf.state = candidate
rf.mu.Unlock()
}
case candidate:
rf.mu.Unlock()
...
case leader:
rf.mu.Unlock()
...
To use the current rf.state
upon switch
, I have to lock at the outside and unlock it at the inside. What bothers me is that for every case I have to unlock for every cases (making the code less clean and readable?). And there is some blocking (e.g. time.After(...)
) going on to make a timer inside every case so I see no way of using defer
to help me. I guess it's some tradeoff I have to make if I don't want to alter the logical structure of this switch
?
答案1
得分: 1
你的代码看起来还不错,但是你可以将其中的部分提取到自己的函数中,以便使用defer
。
func (rf *Raft) runElection() {
// ...
for server := range rf.peers {
tryVote(rf, server)
}
}
func tryVote(rf *Raft, server xxx) {
rf.mu.Lock()
defer rf.mu.Unlock()
if server != rf.me && rf.state == candidate {
go rf.sendRequestVote(server)
}
}
当你的代码变得更加复杂,并且你想要确保互斥锁在从某个代码段返回后绝对被解锁时,这种方法尤其有用。
英文:
While your code looks okay, you can always extract parts into their own functions to make use of defer
.
func (rf *Raft) runElection() {
// ...
for server := range rf.peers {
tryVote(rf, server)
}
}
func tryVote(rf *Raft, server xxx) {
rf.mu.Lock()
defer rf.mu.Unlock()
if server != rf.me && rf.state == candidate {
go rf.sendRequestVote(server)
}
}
This is especially helpful when your code gets more complex and you want to make ABSOLUTELY sure the mutex gets unlocked once you return from some code section.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论