试图在golang截止日期之前获得一把锁?

时间:2019-02-01 23:06:51

标签: go timeout mutex

如何仅尝试立即获得互斥锁,要么立即中止(如 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似乎表明没有这种东西,而且我处理这些问题的方式完全不合时宜。

3 个答案:

答案 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)

我想你在这里问几个不同的问题:

  1. 此工具是否存在于标准libray中?不,不是。您可能会在其他地方找到实现-可以使用标准库(例如原子)来实现

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

  3. 如何设计我的程序,使我不需要它?

(3)的答案更加细微,取决于您的确切问题。您的问题已经说了

  

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

没有提供为什么使用所有CPU而不是检查互斥锁状态的详细信息。

在Go中,每当锁定方案变得平凡时,您通常都希望使用频道。它不应该变慢,应该更易于维护。

答案 2 :(得分:0)

该软件包如何:https://github.com/viney-shih/go-lock。它使用频道信号量golang.org/x/sync/semaphore)解决您的问题。

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
}