使用Golang限制单个可执行文件实例

时间:2014-04-18 21:53:15

标签: windows go

我需要一次只允许一个Golang可执行文件的实例。我不确定如何使用Global Mutex来确保没有其他实例正在运行。

这将在Windows机器上运行。

4 个答案:

答案 0 :(得分:7)

似乎不是一个跨平台的解决方案(除了编写文件,并在开始时查找该文件)。

在Windows上,这是thread reports

  

推荐的方法(以及对我有用的方法)是使用CreateSemaphore function   如果您指定的名称以" Global\"开头,那么信号量对于整个系统是唯一的,第二次尝试打开它将失败。

这是一个kernel32调用,它有一些wrapper in Go available

kostix在评论中添加:

  

查看围绕pkg \ syscall层次结构的Go源代码 - 它包含大量关于如何使用系统调用在Windows上调用DLL的示例(以及如何在Windows API中访问任何内容)

那将是syscall/dll_windows.go。 (这里is a gist

  

odbc package by brainman是Windows上直接API调用的另一个示例 - 可能更容易理解。

api/zapi_windows.go

答案 1 :(得分:4)

我知道这个主题有点陈旧,但我最近在Windows上需要它,我会在这里发布我是如何做到的,以防其他人需要。

向@VonC求助,指出我正确的方向。

var (
    kernel32        = syscall.NewLazyDLL("kernel32.dll")
    procCreateMutex = kernel32.NewProc("CreateMutexW")
)

func CreateMutex(name string) (uintptr, error) {
    ret, _, err := procCreateMutex.Call(
        0,
        0,
        uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(name))),
    )
    switch int(err.(syscall.Errno)) {
    case 0:
        return ret, nil
    default:
        return ret, err
    }
}

// mutexName starting with "Global\" will work across all user sessions
_, err := CreateMutex("SomeMutexName")

我使用更完整的示例创建了一个lib:https://github.com/rodolfoag/gow32

THX!

答案 2 :(得分:3)

您可以使用简单易用的套接字,并且可以真正处理所有内容。

package main

import (
    "fmt"
    "net"
    "os"
    "strings"
)

const (
    INSTANCE_PORT = 9292
)

func main() {
    listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", INSTANCE_PORT))
    if err != nil {
        if strings.Index(err.Error(), "in use") != -1 {
            //optionally send command line arguments to the other instance
            fmt.Fprintln(os.Stderr, "Already running.")
            return
        } else {
            panic(err)
        }
    }

    for {
        conn, err := listener.Accept()
        if err != nil {
            println("Error accept:", err.Error())
            return
        }
        go do_something_with(conn)
    }
}

答案 3 :(得分:2)

您可以调整tendo的python库source

中的代码 他们做的是什么 对于Windows:

创建一个由可执行的绝对路径构成的文件(好吧它是一个库,所以在你的情况下,你可以只定义一个标识符,以防止你"我把可执行文件放在2个地方&#34 ;)

  • 对于Windows:首先尝试删除文件(如果存在),如果没有创建文件os.O_CREAT | os.O_EXCL | os.O_RDWR
  • 对于POSIX兼容系统:首先尝试删除文件(如果已存在),如果没有创建文件并使用fcntl.LOCK_EX | fcntl.LOCK_NB获取锁定

任何失败都意味着程序已在运行

然后您可以使用延迟操作来删除锁定(在posix系统上)并删除文件

Go允许您创建带有构建注释的版本,以根据您的操作系统告知要编译哪个文件,以便

用于unix系统

// +build !windows

package main

import (
    "os"
    "syscall"
)

func create_lock_file(filename string) (*os.File, error) {
    file, err := os.OpenFile(filename, os.O_WRONLY, 0666)
    if err != nil {
        return nil, err
    }
    err = syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
    if err != nil {
        return nil, err
    }
    return file, nil
}

for windows:

// +build !windows

package main

import (
    "os"
)

func create_lock_file(filename string) (*os.File, error) {
    if _, err := os.Stat(filename); err == nil {
        err = os.Remove(filename)
        if err != nil {
            return nil, err
        }

    }
    return os.OpenFile(filename, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666)
}

和测试

package main

import (
    "fmt"
    "time"
)

func main() {

    _, err := create_lock_file("plop.lock")
    if err != nil {
        fmt.Println("error ", err.Error())
    }

    time.Sleep(10 * time.Second)
    fmt.Println("end ")
}

我已经从中创建了一个库that you can find here