为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
PIPIONE 2023-06-05 17:02:19
如何在 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似乎暗示没有这样的事情,而且我处理这些问题的方式完全不可行。
查看完整描述

3 回答

?
明月笑刀无情

TA贡献1828条经验 获得超4个赞

使用缓冲区大小为 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

}


查看完整回答
反对 回复 2023-06-05
?
BIG阳

TA贡献1859条经验 获得超6个赞

我想你在这里问了几件不同的事情:

  1. 标准库中是否存在此功能?不,它没有。您可能可以在其他地方找到实现 - 这可以使用标准库(例如原子)来实现。

  2. 为什么标准库中不存在此功能:您在问题中提到的问题是一个讨论。在 go-nuts 邮件列表上也有几个讨论,有几个 Go 代码开发人员贡献:链接 1链接 2。通过谷歌搜索很容易找到其他讨论。

  3. 我怎样才能设计我的程序,这样我就不需要这个了?

(3) 的答案更加微妙,取决于您的具体问题。你的问题已经说了

可以将它实现为一个工作队列(带有通道和东西),但在那种情况下,衡量和利用所有可用的 CPU 变得更加困难

没有详细说明为什么与检查互斥锁状态相比,使用所有 CPU 会更加困难。

在 Go 中,只要锁定方案变得重要,您通常就需要通道。它不应该更慢,而且应该更易于维护。


查看完整回答
反对 回复 2023-06-05
?
慕雪6442864

TA贡献1812条经验 获得超5个赞

go-lock除了 Lock 和 Unlock 之外,还实现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

}


查看完整回答
反对 回复 2023-06-05
?
Helenr

TA贡献1780条经验 获得超3个赞

PMutex 实现 RTryLock(ctx context.Context) 和 TryLock(ctx context.Context)

// ctx - some context

ctx := context.Background()

mx := mfs.PMutex{}

isLocked := mx.TryLock(ctx)

if isLocked {

    // DO Something

    mx.Unlock()

} else {

    // DO Something else

}


查看完整回答
反对 回复 2023-06-05
  • 3 回答
  • 0 关注
  • 119 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信