英文:
What's the best way to implement a semaphore in Go
问题
我有一个测试程序,我想从命令行运行多个程序副本,并且我需要知道要启动的程序的第一个实例。在Dart中,有人建议我使用以下方法:
RawServerSocket.bind("127.0.0.1", 8087)
如果失败,那么我知道另一个程序已经“锁定”了该端口。这对我来说解决了问题。当程序终止或套接字被显式关闭时,锁定会被释放。
在Go中,我如何实现类似的结果呢?
英文:
I have a test program where I want to run multiple copies of the program from the command-line, and I need to know the first instance of the program to start. In Dart, I do the following which someone suggested to me :
RawServerSocket.bind("127.0.0.1", 8087)
If that fails, then I know that another program has "locked" the port. That solves the problem sufficiently well for me. The lock is released when the program terminates or when the socket is explicitly closed.
How can I achieve a similar result in Go?
答案1
得分: 3
“Unix方式”是在/var/run/目录下创建一个名为
这是我们在Docker中的做法:https://github.com/dotcloud/docker/blob/master/docker/docker.go#L91
如果该文件存在,则表示程序已经启动。
我不建议使用bind方法,它会无谓地锁定一个端口,并且你不能确定其他程序是否正在使用该端口。
英文:
The 'unix way' would be to create a pid file within /var/run/<name>.pid
This is how we do it with Docker https://github.com/dotcloud/docker/blob/master/docker/docker.go#L91
If the file exists, then it means the program is already started.
I would not recommend to use the bind method, it lock a port for nothing and you can't be sure that an other program is not using the port.
答案2
得分: 3
你没有说明你使用的平台。如果你想要跨平台的解决方案,那么在Go语言中打开本地套接字的方法非常简单。以下是在Go语言中实现的示例代码:
package main
import (
"log"
"net"
"time"
)
func main() {
ln, err := net.Listen("tcp", "127.0.0.1:9876")
if err != nil {
log.Fatal("Failed to get lock: ", err)
}
defer ln.Close()
log.Print("Started")
time.Sleep(time.Second * 10)
log.Print("Ending")
}
不使用套接字的跨平台方法相对较难,可能需要编写两套代码,一套用于类Unix系统,一套用于Windows系统。
请注意,病毒检查程序不喜欢在Windows上打开监听套接字的程序...
英文:
You don't say which platform you are using. If you want to be cross platform then the solution of opening a local socket is very easy. Here is how you do it in Go.
package main
import (
"log"
"net"
"time"
)
func main() {
ln, err := net.Listen("tcp", "127.0.0.1:9876")
if err != nil {
log.Fatal("Failed to get lock: ", err)
}
defer ln.Close()
log.Print("Started")
time.Sleep(time.Second * 10)
log.Print("Ending")
}
A cross platform way of doing this without using a socket is quite hard unfortunately and would involve two sets of code one for Unix like systems and one for windows.
Note that virus checkers don't like programs opening listening sockets on windows...
答案3
得分: 2
使用os.OpenFile()
和os.CREATE | os.O_EXCL
标志如何?
file, err := os.OpenFile("lock", os.O_CREATE | os.O_EXCL | os.O_RDWR, 0400)
if err != nil {
// 有其他进程已经获取了锁。
}
defer file.Close()
defer os.Remove("lock") // 忽略这里的错误!
我没有编译或测试过这段代码,但它应该可以工作,无论如何,你可以理解这个思路...
英文:
What about using os.OpenFile()
with os.CREATE | os.O_EXCL
flag?
file, err := os.OpenFile("lock", os.O_CREATE | os.O_EXCL | os.O_RDWR, 0400)
if err != nil {
// Someone else has acquired the lock.
}
defer file.Close()
defer os.Remove("lock") // Ignoring errors here!
I haven't compiler or tested this, but it should work and in any case, you get the idea...
答案4
得分: 0
使用github.com/gofrs/flock
包,使用类似以下代码来实现你的目的:
import "github.com/gofrs/flock"
fileLock := flock.New("/var/lock/go-lock.lock")
locked, err := fileLock.TryLock()
if err != nil {
// 处理加锁错误
}
if locked {
// 处理“第一个进入”的情况
fileLock.Unlock()
} else {
// 处理后续情况
}
更多信息请参考 https://github.com/gofrs/flock。
英文:
Use the github.com/gofrs/flock
package, using code something like this for your purposes:
import "github.com/gofrs/flock"
fileLock := flock.New("/var/lock/go-lock.lock")
locked, err := fileLock.TryLock()
if err != nil {
// handle locking error
}
if locked {
// handle "first one in" case
fileLock.Unlock()
} else {
// handle subsequent cases
}
For more info see https://github.com/gofrs/flock .
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论