post Image
Go言語でプロセス間同期処理

Goにはプロセス間の同期がない?

goルーチンという素晴らしいスレッド(厳密には違うようですが)の仕組みを提供してくれているGolangですが、プロセス間の制御をする仕組みがありませんでした。

複数のプロセスから、1つのファイルにログを記録したいと考えていました。
「golangにファイルロックの仕組みはないのですか?」という質問へのAndrew Gerrandの回答は「syscall.Flockを使え」というもの。
もちろんこれはWindowsでは使えない。(コンパイルエラーになってしまう)
(回答は2011年のものだが、今も事情は変わっていない様子)
https://groups.google.com/forum/#!topic/golang-nuts/L6KKPCxysXw

LinuxやMacだけならばこれでいいのですが、Windowsにも対応したい。Javaではなく、Goを使うのだから、せめてビジネスロジックだけでも違うソースにはしたくない。。。

というわけで、作ってみました。

Windowsでのプロセス間同期

WindowsAPIをGolangから呼び出します。
代表的なミューテックスを使いました。

lock_windows.go
package util
import (
    "errors"
    "fmt"
    "os"
    "syscall"
    "unsafe"
)

// ミューテックスハンドルを保持する。
type LockHandle struct {
    handle uintptr
    isLock bool
}

const (
    wAIT_OBJECT_0  int = 0
    wAIT_ABANDONED int = 128
    wAIT_TIMEOUT   int = 258
)

var ErrBusy = errors.New("Locked by other process.")

// プロセス間で共通に使用する名前を指定する。
func InitLock(name string) (*LockHandle, error) {
    mutexName := fmt.Sprintf("Global\\%s", name)
    hMutex, _, err := procCreateMutexW.Call(
        0, 0, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(mutexName))))
    if hMutex == 0 {
        fmt.Fprintf(os.Stderr, "Failed InitMutexW() err = %v", err)
        return nil, err
    }
    return &LockHandle{hMutex, false}, nil
}

// ロックを開始する。
// 引数でタイムアウト時間(ミリ秒)を指定する。
func (l *LockHandle) Lock(timeout_milisec int) error {
    r1, _, err := procWaitForSingleObject.Call(l.handle, uintptr(timeout_milisec))
    if int(r1) == wAIT_OBJECT_0 || int(r1) == wAIT_ABANDONED {
        // Lock成功
        l.isLock = true
        return nil
    } else if int(r1) == wAIT_TIMEOUT {
        msg := fmt.Sprintf("Lock Timeout. Detail( %v )", err)
        fmt.Fprintf(os.Stderr, "%v\n", msg)
        return ErrBusy
    }
    return fmt.Errorf("Lock Unknown Error. Detail( %v )", err)
}

// ロック中であれば、解除する。
func (l *LockHandle) Unlock() error {
    if l.isLock {
        r1, _, err := procReleaseMutex.Call(l.handle)
        if int(r1) == 0 { // 失敗
            return fmt.Errorf("Unlock Error. Detail( %v )", err)
        }
        l.isLock = false
        return nil
    }
    return nil
}

// InitMutexで確保したミューテックスオブジェクトを破棄する。
func (l *LockHandle) TermLock() error {
    procCloseHandle.Call(l.handle)
    return nil
}

WindowsAPIを直接呼んでいるのはこちら。

syscall_windows.go
package util
import (
    "syscall"
)

// DLLハンドル
type goDll struct {
    dll *syscall.DLL
}

var (
    kernel32_dll = loadDLL("kernel32.dll")

    procCreateMutexW        = kernel32_dll.findProc("CreateMutexW")
    procWaitForSingleObject = kernel32_dll.findProc("WaitForSingleObject")
    procReleaseMutex        = kernel32_dll.findProc("ReleaseMutex")
    procCloseHandle         = kernel32_dll.findProc("CloseHandle")
)

func loadDLL(name string) *goDll {
    dll, err := syscall.LoadDLL(name)
    if err != nil {
        panic(err)
    }
    goDll := new(goDll)
    goDll.dll = dll
    return goDll
}

func (c *goDll) findProc(name string) *syscall.Proc {
    proc, err := c.dll.FindProc(name)
    if err != nil {
        panic(err)
    }
    return proc
}

実際の使い方はこちら

main.go
func main() {
    locker, lockErr := util.InitLock(lockName)
    if lockErr != nil {
        return lockErr
    }
    defer locker.TermLock()

    //lockが必要な処理を開始
    locker.Lock(10)

    //lockが不要になった
    locker.Unlock()
}

なんとなく形になったかな?
Linux側が「lock_linux.go」を同じインタフェースで作っておけば、使い回せそう。

ここで作ったライブラリは、GithubにもUpしています。

https://github.com/unirita/cuto


『 Go 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。