试图在戈兰获得一个有截止日期的锁

  • 本文关键字:一个 日期 go timeout mutex
  • 更新时间 :
  • 英文 :


如何在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(),请参阅下面的Stats示例。

/* 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()中返回廉价响应,以防我们无法及时获取锁。

不幸的是,同步。互斥仅具有Lock()Unlock()函数。无法潜在地获取锁。有什么简单的方法可以代替吗?我是不是从一个完全错误的角度来处理这类问题,有没有一种不同的、更"主动"的方法来解决它们?或者,如果我想解决这些问题,我是否必须实现我自己的Mutex库?我知道问题6123,这似乎表明没有这样的事情,我处理这些问题的方式完全不符合标准。

使用缓冲区大小为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. 为什么这个设施不存在于标准库中:你在问题中提到的问题只是一个讨论。还有一些关于go nuts邮件列表的讨论,一些go代码开发人员对此做出了贡献:链接1,链接2。通过谷歌搜索很容易找到其他讨论。

  3. 我如何设计我的程序,这样我就不需要这个了?

(3)的答案更微妙,取决于您的确切问题。你的问题已经表明

可以将其实现为工作队列(具有通道和东西),但在这种情况下,测量和利用所有可用的CPU

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

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

这个包怎么样:https://github.com/viney-shih/go-lock。它使用通道信号量(golang.org/x/sync/speagle)来解决您的问题。

go-lock除了实现锁定和解锁之外还实现TryLockTryLockWithTimeoutTryLockWithContext功能。它提供了控制资源的灵活性。

示例:

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
}

包中的PMutexhttps://github.com/myfantasy/mfs

PMutex实现RTryLock(ctx-context.context)和TryLock(ctx-context.Content)

// ctx - some context
ctx := context.Background()
mx := mfs.PMutex{}
isLocked := mx.TryLock(ctx)
if isLocked {
// DO Something
mx.Unlock()
} else {
// DO Something else
}

相关内容

  • 没有找到相关文章