Какой лучший способ реализовать семафор в Go

У меня есть тестовая программа, в которой я хочу запустить несколько копий программы из командной строки, и мне нужно знать первый экземпляр программы для запуска. В Dart я делаю следующее, что мне кто-то предложил:

RawServerSocket.bind("127.0.0.1", 8087)

Если это не удается, то я знаю, что другая программа "заблокировала" порт. Это решает проблему достаточно хорошо для меня. Блокировка снимается, когда программа завершается или когда сокет явно закрыт.

Как я могу достичь аналогичного результата в Go?

4 ответа

Решение

Вы не говорите, какую платформу вы используете. Если вы хотите быть кроссплатформенным, то решение открытия локального сокета очень простое. Вот как вы делаете это в 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...

"Unix-способ" заключается в создании pid-файла в /var/run/.pid. Вот как мы это делаем с Docker https://github.com/dotcloud/docker/blob/master/docker/docker.go#L91 Если файл существует, значит, программа уже запущена.

Я не рекомендовал бы использовать метод bind, он блокирует порт даром, и вы не можете быть уверены, что другая программа не использует этот порт.

Как насчет использования 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 {
        // Someone else has acquired the lock.
}
defer file.Close()
defer os.Remove("lock") // Ignoring errors here!

Я не компилировал и не проверял это, но он должен работать, и в любом случае, вы поняли...

Использоватьgithub.com/gofrs/flockpackage, используя примерно такой код для ваших целей:

      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
}

Для получения дополнительной информации см. https://github.com/gofrs/flock .

Другие вопросы по тегам