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つがあるようで。
_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 がインストールされた。
_01_mutex.py
python 01_mutex.py で実行すると、1秒毎に、0 から 9 までの数字を出す。
DOS窓を複数開いておいて、どこかのDOS窓でスクリプトを動かしてから、すかさず別のDOS窓で同じスクリプトを動かしてみると、後から実行したほうは「〜 already exists」と出力して即座に終了してくれた。たしかに、多重起動を禁止するスクリプトになってくれた模様。
ちょっとよく分からなかったのが、win32event.ReleaseMutex(mtx) を呼ぶと必ずエラーになること。これは呼ばなくてもいいのだろうか…? クローズ処理として win32api.CloseHandle(mtx) か mtx.Close() は呼んでおくらしいけど…。巷のサンプルを眺めても、win32event.ReleaseMutex(mtx) を呼んでる事例は見かけなかったので、呼ばなくてもいいのかもしれない。
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
インポートするモジュール数は少なくなってくれた気がする。ただ、ソースが少し分かりづらく…。いや、あまり違いはないか…。
ctypes 経由で呼び出す版は、最後に .ReleaseMutex() と .CloseHandle() を呼び出してもエラーにならなかった。
_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() を呼び出してもエラーにならなかった。
[ ツッコむ ]
以上です。