如何仅尝试立即获得互斥锁,要么立即中止(如 TryLock 在其他实现中所做的那样),要么观察某种形式的期限(基本上是 LockBefore < / em>)?
我现在可以想到2种情况,这将对您大有帮助,并且我正在寻找某种解决方案。第一个是:接收等待时间敏感的请求(例如web服务)一个CPU重服务。在这种情况下,您可能想要执行以下 RPCService 示例。可以将其实现为工作队列(带有通道和填充),但是在这种情况下,衡量和利用所有可用CPU变得更加困难。也可以接受的是,在您获得锁之前,您的代码可能已经超过了最后期限,但这不是理想的选择,因为它浪费了一些资源,并且意味着我们无法做类似“性能下降的临时任务”的事情。响应”。
/* Example 1: LockBefore() for latency sensitive code. */
func (s *RPCService) DoTheThing(ctx context.Context, ...) ... {
if s.someObj[req.Parameter].mtx.LockBefore(ctx.Deadline()) {
defer s.someObj[req.Parameter].mtx.Unlock()
... expensive computation based on internal state ...
} else {
return s.cheapCachedResponse[req.Parameter]
}
}
另一种情况是,您有一堆应该被触摸但可能被锁定的对象,并且应在一定时间内完成触摸的对象(例如,更新某些统计信息)。在这种情况下,您也可以使用 LockBefore()或某种形式的 TryLock(),请参见下面的统计信息示例。
/* Example 2: TryLock() for updating stats. */
func (s *StatsObject) updateObjStats(key, value interface{}) {
if s.someObj[key].TryLock() {
defer s.someObj[key].Unlock()
... update stats ...
... fill in s.cheapCachedResponse ...
}
}
func (s *StatsObject) UpdateStats() {
s.someObj.Range(s.updateObjStats)
}
为了易于使用,我们假设在上述情况下,我们正在谈论相同的 s.someObj 。任何对象都可能长时间被 DoTheThing()操作阻止,这意味着我们想在 updateObjStats 中跳过它。另外,我们希望确保在无法获取时间锁定的情况下,在 DoTheThing()中返回廉价响应。
不幸的是,仅sync.Mutex独家具有功能 Lock()和 Unlock()。无法潜在地获得锁。有一些简单的方法可以做到这一点吗?我是从完全错误的角度来解决此类问题吗,有没有其他解决方案,更“可行”了?还是要解决这些问题,是否必须实现自己的Mutex库?我知道the documentation似乎表明没有这种东西,而且我处理这些问题的方式完全不合时宜。
答案 0 :(得分:3)
使用缓冲区大小为1的通道作为互斥体。
l := make(chan struct{}, 1)
锁定:
l <- struct{}{}
解锁:
<-l
尝试锁定:
select {
case l <- struct{}{}:
// lock acquired
<-l
default:
// lock not acquired
}
尝试超时:
select {
case l <- struct{}{}:
// lock acquired
<-l
case <-time.After(time.Minute):
// lock not acquired
}
答案 1 :(得分:2)
我想你在这里问几个不同的问题:
此工具是否存在于标准libray中?不,不是。您可能会在其他地方找到实现-可以使用标准库(例如原子)来实现。
为什么标准库中不存在此功能:您在问题中提到的问题只是一次讨论。在go-nuts邮件列表上也有一些讨论,其中有多个Go代码开发人员参与其中:link 1,link 2。通过谷歌搜索很容易找到其他讨论。
如何设计我的程序,使我不需要它?
(3)的答案更加细微,取决于您的确切问题。您的问题已经说了
可以将其实现为工作队列(带有通道和 东西),但在这种情况下,衡量和 利用所有可用的CPU
没有提供为什么使用所有CPU而不是检查互斥锁状态的详细信息。
在Go中,每当锁定方案变得平凡时,您通常都希望使用频道。它不应该变慢,应该更易于维护。
答案 2 :(得分:0)
该软件包如何:https://github.com/viney-shih/go-lock。它使用频道和信号量(golang.org/x/sync/semaphore)解决您的问题。
go-lock
除了锁定和解锁之外,还实现了TryLock
,TryLockWithTimeout
和TryLockWithContext
功能。它提供了控制资源的灵活性。
示例:
package main
import (
"fmt"
"time"
"context"
lock "github.com/viney-shih/go-lock"
)
func main() {
casMut := lock.NewCASMutex()
casMut.Lock()
defer casMut.Unlock()
// TryLock without blocking
fmt.Println("Return", casMut.TryLock()) // Return false
// TryLockWithTimeout without blocking
fmt.Println("Return", casMut.TryLockWithTimeout(50*time.Millisecond)) // Return false
// TryLockWithContext without blocking
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
fmt.Println("Return", casMut.TryLockWithContext(ctx)) // Return false
// Output:
// Return false
// Return false
// Return false
}