mieki256's diary



2022/08/14() [n年前の日記]

#1 [python] Pythonスクリプトの多重起動を抑止したい

Windows10 x64 21H2 + Python 3.8.10、3.9.13 上で、Pythonスクリプトの多重起動を抑止したい。

参考ページ。 :

以下のページを参考にして試してみた。

_Creating a single instance application < Python recipes < ActiveState Code
_Ptyhonでプロセス間排他を試す(Windows限定) | The only neEt thing to do.
_windowsで実行中のプロセスのフルパスを、pythonから調べたい
_多重起動禁止処理 for Windows
_benhoyt/namedmutex: namedmutex.py, a simple ctypes wrapper for Win32 named mutexes
_Windows上で多重起動を防止する方法
_Module win32event

Windowsの場合、Mutex なるものを使うと多重起動してるかどうかを判別することができる模様。キーワードとして「python CreateMutex」でググれば情報に辿り着けそう。

その Mutex を利用する方法として、以下の2つがあるようで。
  • win32event、win32api、winerror等を経由して呼び出す。
  • ctypes モジュールで呼び出す。

ソースその1。win32event版。 :

まずは win32event等を経由して使う方法を試してみた。動作には pywin32 のインストールが必要。今回は pywin32 304 がインストールされた。

pip install pywin32

_01_mutex.py
import win32event
import win32api
import winerror
import win32security
import time
import sys

MUTEXNAME = "python_mutex_sample_01"


def main():
    sa = win32security.SECURITY_ATTRIBUTES()
    sa.SECURITY_DESCRIPTOR.SetSecurityDescriptorDacl(True, None, False)

    mtx = win32event.CreateMutex(sa, False, MUTEXNAME)
    err = win32api.GetLastError()
    if not mtx or err == winerror.ERROR_ALREADY_EXISTS:
        # Process exists
        print("%s already exists" % MUTEXNAME)
        sys.exit(0)
    else:
        # New process
        print("New process.")
        for i in range(10):
            print(i)
            time.sleep(1)

    if mtx:
        # win32event.ReleaseMutex(mtx)
        # win32api.CloseHandle(mtx)
        mtx.Close()


if __name__ == '__main__':
    main()
  • Mutex を作る際は、他のプロセスと被らないような独自の文字列を渡す。
  • CreateMutex() を呼んだ直後にエラー情報を調べることで、その Mutex が既にあるかどうかが分かる模様。
  • 処理が終わったら、mtx.Close() を呼んでハンドルをクローズする。

python 01_mutex.py で実行すると、1秒毎に、0 から 9 までの数字を出す。

DOS窓を複数開いておいて、どこかのDOS窓でスクリプトを動かしてから、すかさず別のDOS窓で同じスクリプトを動かしてみると、後から実行したほうは「〜 already exists」と出力して即座に終了してくれた。たしかに、多重起動を禁止するスクリプトになってくれた模様。

ちょっとよく分からなかったのが、win32event.ReleaseMutex(mtx) を呼ぶと必ずエラーになること。これは呼ばなくてもいいのだろうか…? クローズ処理として win32api.CloseHandle(mtx) か mtx.Close() は呼んでおくらしいけど…。巷のサンプルを眺めても、win32event.ReleaseMutex(mtx) を呼んでる事例は見かけなかったので、呼ばなくてもいいのかもしれない。

ソースその2。ctypes版。 :

ctypesを使って呼び出す方法も試してみた。

_02_mutex_ctypes.py
import ctypes
import time

MUTEXNAME = "python_mutex_sample_02"


def main():
    knl32 = ctypes.windll.Kernel32
    mtx = knl32.CreateMutexA(0, 1, MUTEXNAME)
    result = knl32.WaitForSingleObject(mtx, 0)
    if result != 0:
        print("%s already exists" % MUTEXNAME)
    else:
        print("New process.")
        for i in range(10):
            print(i)
            time.sleep(1)

    knl32.ReleaseMutex(mtx)
    knl32.CloseHandle(mtx)


if __name__ == '__main__':
    main()

インポートするモジュール数は少なくなってくれた気がする。ただ、ソースが少し分かりづらく…。いや、あまり違いはないか…。

ctypes 経由で呼び出す版は、最後に .ReleaseMutex() と .CloseHandle() を呼び出してもエラーにならなかった。

以上です。

過去ログ表示

Prev - 2022/08 - Next
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project