猿问

试图在 golang 中获取具有截止日期的锁?

如何在 go 中只尝试获取类似互斥锁的锁,要么立即中止(就像TryLock在其他实现中所做的那样),要么通过观察某种形式的截止日期(基本上是LockBefore)?


我现在可以想到两种情况,这会非常有帮助,而我正在寻找某种解决方案。第一个是:接收延迟敏感请求的 CPU 密集型服务(例如 Web 服务)。在这种情况下,您可能希望执行类似于下面的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 库?我知道issue 6123似乎暗示没有这样的事情,而且我处理这些问题的方式完全不可行。


PIPIONE
浏览 154回答 3
3回答

明月笑刀无情

使用缓冲区大小为 1 的通道作为互斥锁。l := make(chan struct{}, 1)锁:l <- struct{}{}开锁:<-l尝试锁定:select {case l <- struct{}{}:&nbsp; &nbsp; // lock acquired&nbsp; &nbsp; <-ldefault:&nbsp; &nbsp; // lock not acquired}尝试超时:select {case l <- struct{}{}:&nbsp; &nbsp; // lock acquired&nbsp; &nbsp; <-lcase <-time.After(time.Minute):&nbsp; &nbsp; // lock not acquired}

BIG阳

我想你在这里问了几件不同的事情:标准库中是否存在此功能?不,它没有。您可能可以在其他地方找到实现 - 这可以使用标准库(例如原子)来实现。为什么标准库中不存在此功能:您在问题中提到的问题是一个讨论。在 go-nuts 邮件列表上也有几个讨论,有几个 Go 代码开发人员贡献:链接 1,链接 2。通过谷歌搜索很容易找到其他讨论。我怎样才能设计我的程序,这样我就不需要这个了?(3) 的答案更加微妙,取决于您的具体问题。你的问题已经说了可以将它实现为一个工作队列(带有通道和东西),但在那种情况下,衡量和利用所有可用的 CPU 变得更加困难没有详细说明为什么与检查互斥锁状态相比,使用所有 CPU 会更加困难。在 Go 中,只要锁定方案变得重要,您通常就需要通道。它不应该更慢,而且应该更易于维护。

慕雪6442864

go-lock除了 Lock 和 Unlock 之外,还实现TryLock,TryLockWithTimeout和功能。TryLockWithContext它提供了控制资源的灵活性。例子:package mainimport (    "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}

Helenr

PMutex 实现 RTryLock(ctx context.Context) 和 TryLock(ctx context.Context)// ctx - some contextctx := context.Background()mx := mfs.PMutex{}isLocked := mx.TryLock(ctx)if isLocked {    // DO Something    mx.Unlock()} else {    // DO Something else}
随时随地看视频慕课网APP

相关分类

Go
我要回答