1 回答
TA贡献1895条经验 获得超7个赞
当互斥体被锁定时,对互斥锁()的
所有其他调用都将被阻止,直到首先调用互斥锁()
为止。
因此,当处理程序正在运行(并持有互斥锁)时,所有其他请求将在调用时被阻止。Lock()
注意:如果您的处理程序由于您提前返回(使用语句)而无法正常完成,或者它崩溃了,您的互斥锁将保持锁定状态,因此所有进一步的请求都将被阻止。return
一个好的做法是在互斥体被锁定后立即使用 defer
来解锁它:
s.mu.Lock() defer s.mu.Unlock()
这确保了无论您的函数如何结束(可能正常结束,返回或恐慌),都将被调用。Unlock()
尝试尽可能少地保持锁定,以最大程度地减少其他请求的阻塞时间。虽然在进入处理程序时正确锁定并在返回之前仅解锁可能很方便,但如果在处理程序的“生存期”内不使用受保护的资源,则仅在使用共享资源时才锁定和解锁。例如,如果要保护对文件的并发访问,请锁定互斥锁,读/写文件,并在完成后立即解锁互斥锁。如何处理读取数据以及如何组装和发送响应不应阻止其他请求。当然,在使用解锁时,它可能不会像它应该的那样早运行(当您完成共享资源时)。因此,在某些情况下,可以使用 不使用 ,或者访问共享资源的代码可能会被移动到命名或未命名(匿名)函数,以便仍然能够使用 。defer
defer
defer
同步。互斥体
不支持“扫视”状态,也不支持“尝试锁定”操作。这意味着使用时,您无法向客户端发出它必须等待的信号,因为处理请求正在等待另一个请求完成。如果您需要此类功能,则可以使用通道。容量为 1 的缓冲通道可以实现此功能:“锁定”操作在通道上发送值,“解锁”操作从通道接收值。目前为止,一切都好。“try-lock”操作可以是“有条件的”发送操作:使用带有事例的 select
语句,您可以检测到您现在无法锁定,因为它已被锁定,您可以执行其他操作或同时执行其他操作,并在以后重试锁定。sync.Mutex
default
下面是一个示例:它可能看起来像这样:
var lock = make(chan struct{}, 1)
func handler(w http.ResponseWriter, r *http.Request) {
// Try locking:
select {
case lock <- struct{}{}:
// Success: proceed
defer func() { <-lock }() // Unlock deferred
default:
// Another handler would block us, send back an "error"
http.Error(w, "Try again later", http.StatusTooManyRequests)
return
}
time.Sleep(time.Second * 2) // Simulate long computation
io.WriteString(w, "Done")
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
上面的简单示例如果另一个请求持有锁定,则会立即返回错误。你可以选择在这里做不同的事情:你可以把它放在一个循环中,在放弃并返回错误之前重试几次(在迭代之间稍微睡觉)。您可以在尝试锁定时使用超时,并且只有在一段时间内无法获得锁定时才接受“失败”(请参阅时间。After() 和上下文。WithTimeout())。当然,如果我们使用某种超时,则必须删除该案例(如果其他案例都不能立即进行,则立即选择该案例)。defaultdefault
当我们处于它(超时)时,由于我们已经在使用 ,因此我们可以合并监视请求的上下文是一个好处:如果它被取消,我们应该提前终止并返回。为此,我们可以通过添加从上下文的 done 通道接收的案例来执行此操作,例如 。selectcase <-r.Context().Done():
下面是一个示例,如何简单地使用:select
var lock = make(chan struct{}, 1)
func handler(w http.ResponseWriter, r *http.Request) {
// Wait 1 sec at most:
ctx, cancel := context.WithTimeout(r.Context(), time.Second)
defer cancel()
// Try locking:
select {
case lock <- struct{}{}:
// Success: proceed
defer func() { <-lock }() // Unlock deferred
case <-ctx.Done():
// Timeout or context cancelled
http.Error(w, "Try again later", http.StatusTooManyRequests)
return
}
time.Sleep(time.Second * 2) // Simulate long computation
io.WriteString(w, "Done")
}
- 1 回答
- 0 关注
- 99 浏览
添加回答
举报