Golang:如何使信号量超时?

问题描述:

Golang中的信号量是通过以下渠道实现的:

Semaphore in Golang is implemented with a channel:

一个例子是这样的: https://sites.google.com/site/gopatterns/concurrency/semaphores

An example is this: https://sites.google.com/site/gopatterns/concurrency/semaphores

上下文:

我们有数百台服务器,并且我们希望限制访问共享资源.因此,对于给定的资源,我们希望使用信号量将那些服务器的访问限制为仅5个并发访问.为此,我们计划使用锁定服务器.当机器访问资源时,它将首先向锁服务器注册它正在通过密钥访问资源.然后,完成后,它将向锁定服务器发送另一个请求,以说已完成并释放信号量.这样可以确保我们将对这些资源的访问限制为最大数量的并发访问.

We have a few hundred servers and there are shared resources that we want to limit access to. So for a given resource, we want to use a semaphore to limit access to only 5 concurrent access by those servers. In order to do that, we are planning to use a lock server. When a machine accesses the resource, it will first register with the lock server that it is accessing the resource by a key. And then when it is done, it will send another request to the lock server to say that its done and release the semaphore. This ensures that we limit access to those resources to a maximal number of concurrent access.

问题::如果出现问题,请妥善处理.

Problem: Want to handle this gracefully if something goes wrong.

问题:

您如何在信号量上实现超时?

How do you go about implementing a timeout on the semaphore?

示例:

比方说,我的信号量大小为5.同时有10个进程试图获取该信号量的锁,因此在这种情况下,只有5个进程将获得该信号量.

Let's say I have a semaphore size of 5. There are simultaneously 10 processes trying to acquire a lock in the semaphore so in this case only 5 will acquire it.

有时,进程会死掉而没有响应(真正的原因很难解释,但基本上有时进程可能无法解锁),这会导致问题,因为现在信号量中的空间已被永久锁定.

Sometimes, processes will die without responding (the real reason is a bit complicated to explain, but basically sometimes the process might not unlock it) so that causes a problem as a space in the semaphore is now permanently locked.

所以我想对此超时.这里有一些问题:

So I would like to have a timeout on this. Here are some issues:

该过程将在2秒到60分钟之间的任何时间运行.

The processes will run from anywhere between 2 seconds up to 60 minutes.

我们有一些竞争条件,因为如果超时,然后进程尝试对其进行解锁,则我们将信号灯解锁了两次,而不是一次.反之亦然,我们首先将其解锁,然后超时.

We have some race conditions, because if it times out and then the process tries to unlock it, then we have unlocked the semaphore twice instead of once. And vice versa, we unlock it first and then it timesout.

如何采用上面建议的模式并将其转换为带有超时的线程安全信号灯?

How do I take the suggested pattern posted above and turn this into a thread-safe semaphore with timeouts?

由于您在进行分布式锁定服务,因此我假设您的锁定服务器在端口上侦听,并且当您接受()连接时,您会循环连接,等待命令在每个连接的goroutine中.当套接字丢失(即:远程节点崩溃)时,该goroutine退出

Since you are making a distributed lock service, I assume your lock server listens on a port, and when you accept() a connection you loop, waiting for commands in a goroutine per connection. And that goroutine exits when the socket is dropped (ie: remote node crash)

因此,假设这是正确的,那么您可以做几件事.

So, assuming that is true, you can do a couple things.

1)创建一个深度与匹配多少个并发锁的通道 2)锁定时,向频道发送一条消息(如果已满,它将阻止) 3)解锁时,只需阅读频道中的消息 4)您可以推迟release()"(如果您已经锁定,则release会消耗一条消息)

1) create a channel with a depth matching how many concurrent locks 2) when you lock, send a message to the channel (it will block if full) 3) when you unlock, just read a message from the channel 4) you can "defer release()" (where release consumes a message if you have already locked)

这是一个粗略的工作示例,除了套接字之外的所有内容. 希望这是有道理的. http://play.golang.org/p/DLOX7m8m6q

Here's a rough working example, all but the socket stuff. Hopefully it makes sense. http://play.golang.org/p/DLOX7m8m6q

package main

import "fmt"

import "time"

type Locker struct {
    ch chan int
    locked bool
}

func (l *Locker) lock(){
    l.ch <- 1
    l.locked=true
}
func (l *Locker) unlock() {
    if l.locked { // called directly or via defer, make sure we don't unlock if we don't have the lock
        l.locked = false // avoid unlocking twice if socket crashes after unlock
        <- l.ch
    }
}

func dostuff(name string, locker Locker) {
    locker.lock()
    defer locker.unlock()
    fmt.Println(name,"Doing stuff")
    time.Sleep(1 * time.Second)
}

func main() {
    ch := make(chan int, 2)
    go dostuff("1",Locker{ch,false})
    go dostuff("2",Locker{ch,false})
    go dostuff("3",Locker{ch,false})
    go dostuff("4",Locker{ch,false})
    time.Sleep(4 * time.Second)
}