プログラムのインスタンスを1つだけ実行するPython的な方法はありますか?
私が思いついた唯一の合理的な解決策は、あるポートでサーバーとして実行しようとすることです。その後、同じポートにバインドしようとする2番目のプログラムが失敗します。しかし、それは本当に素晴らしいアイデアではありません、おそらくこれよりも軽量なものがあるのでしょうか?
(プログラムが時々失敗することが予想されること、すなわちsegfaultを考慮に入れてください-したがって、「ロックファイル」のようなものが機能しません)
プログラムのインスタンスを1つだけ実行するPython的な方法はありますか?
私が思いついた唯一の合理的な解決策は、あるポートでサーバーとして実行しようとすることです。その後、同じポートにバインドしようとする2番目のプログラムが失敗します。しかし、それは本当に素晴らしいアイデアではありません、おそらくこれよりも軽量なものがあるのでしょうか?
(プログラムが時々失敗することが予想されること、すなわちsegfaultを考慮に入れてください-したがって、「ロックファイル」のようなものが機能しません)
回答:
次のコードはその仕事をするはずです、それはクロスプラットフォームであり、Python 2.4-3.2で実行されます。Windows、OS X、Linuxでテストしました。
from tendo import singleton
me = singleton.SingleInstance() # will sys.exit(-1) if other instance is running
最新のコードバージョンはsingleton.pyで入手できます。してくださいここでは、ファイルのバグ。
以下のいずれかの方法を使用して、tendをインストールできます。
easy_install tendopip install tendozgodaによる別の質問で見つかったシンプルなクロスプラットフォームソリューション:
import fcntl
import os
import sys
def instance_already_running(label="default"):
"""
Detect if an an instance with the label is already running, globally
at the operating system level.
Using `os.open` ensures that the file pointer won't be closed
by Python's garbage collector after the function's scope is exited.
The lock will be released when the program exits, or could be
released if the file pointer were closed.
"""
lock_file_pointer = os.open(f"/tmp/instance_{label}.lock", os.O_WRONLY)
try:
fcntl.lockf(lock_file_pointer, fcntl.LOCK_EX | fcntl.LOCK_NB)
already_running = False
except IOError:
already_running = True
return already_running
S.Lottの提案によく似ていますが、コードが含まれています。
fcntlWindowsにはモジュールがありません(機能はエミュレートできます)。
fg。つまり、正常に機能しているように見えます(つまり、アプリはまだアクティブですが、一時停止されているため、ロックは維持されます)。
lock_file_pointer = os.open(lock_path, os.O_WRONLY | os.O_CREAT)
このコードはLinux固有です。「抽象的な」UNIXドメインソケットを使用しますが、シンプルで古くなったロックファイルを残しません。特別に予約されたTCPポートを必要としないため、上記のソリューションよりも優先します。
try:
import socket
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
## Create an abstract socket, by prefixing it with null.
s.bind( '\0postconnect_gateway_notify_lock')
except socket.error as e:
error_code = e.args[0]
error_string = e.args[1]
print "Process already running (%d:%s ). Exiting" % ( error_code, error_string)
sys.exit (0)
一意の文字列postconnect_gateway_notify_lockを変更して、単一のインスタンスを適用する必要がある複数のプログラムを許可できます。
Pythonで十分かどうかはわかりませんが、Javaの世界では、定義されたポートでリッスンすることは、非常に広く使用されているソリューションです。これは、すべての主要なプラットフォームで機能し、プログラムのクラッシュに問題がないためです。
ポートをリッスンするもう1つの利点は、実行中のインスタンスにコマンドを送信できることです。たとえば、ユーザーがもう一度プログラムを起動したときに、実行中のインスタンスにコマンドを送信して、別のウィンドウを開くように指示することができます(たとえば、Firefoxが実行していることです。TCPポートを使用しているか、名前付きパイプを使用しているか、またはそのようなものです」
import socket; s = socket.socket(socket.AF_INET, socket.SOCK_STREAM); s.bind(('localhost', DEFINED_PORT))ます。 OSError別のプロセスが同じポートにバインドされている場合に発生します。
これまでにpythonを記述したことはありませんが、crondによって2回以上開始されるのを防ぐために、これはmycheckpointに実装したものです。
import os
import sys
import fcntl
fh=0
def run_once():
global fh
fh=open(os.path.realpath(__file__),'r')
try:
fcntl.flock(fh,fcntl.LOCK_EX|fcntl.LOCK_NB)
except:
os._exit(0)
run_once()
これを別の問題(http://stackoverflow.com/questions/2959474)に投稿した後、Slava-Nの提案を見つけました。これは関数として呼び出され、実行中のスクリプトファイル(pidファイルではなく)をロックし、スクリプトが終了する(通常またはエラー)までロックを維持します。
pidファイルを使用します。「/ path / to / pidfile」という既知の場所があり、起動時に次のようなことをします(私は事前にコーヒーを飲んでいて、それほどハードに働きたくないので、部分的に疑似コードです)。
import os, os.path
pidfilePath = """/path/to/pidfile"""
if os.path.exists(pidfilePath):
pidfile = open(pidfilePath,"r")
pidString = pidfile.read()
if <pidString is equal to os.getpid()>:
# something is real weird
Sys.exit(BADCODE)
else:
<use ps or pidof to see if the process with pid pidString is still running>
if <process with pid == 'pidString' is still running>:
Sys.exit(ALREADAYRUNNING)
else:
# the previous server must have crashed
<log server had crashed>
<reopen pidfilePath for writing>
pidfile.write(os.getpid())
else:
<open pidfilePath for writing>
pidfile.write(os.getpid())
つまり、言い換えると、pidfileが存在するかどうかを確認しています。そうでない場合は、pidをそのファイルに書き込みます。pidfileが存在する場合は、pidが実行中のプロセスのpidかどうかを確認します。その場合は、別のライブプロセスが実行されているので、シャットダウンしてください。そうでない場合は、前のプロセスがクラッシュしたため、ログに記録し、古いファイルの代わりに独自のPIDをファイルに書き込みます。その後続行します。
すでに別のスレッドで同様の質問への回答を見つけたので、完全を期すために、名前付きミューテックスを使用してWindowsで同じことを行う方法を参照してください。
ロックファイルを使用することは、UNIXでは非常に一般的なアプローチです。クラッシュした場合は、手動でクリーンアップする必要があります。ファイルにPIDを保存し、起動時にこのPIDを持つプロセスがあるかどうかを確認し、ない場合はロックファイルを上書きすることができます。(ただし、read-file-check-pid-rewrite-fileの周りにもロックが必要です)。os -packageでpidの取得とチェックに必要なものが見つかります。特定のpidを持つプロセスが存在するかどうかを確認する一般的な方法は、致命的でないシグナルを送信することです。
他の代替案は、これをflockまたはposixセマフォと組み合わせることです。
sauaが提案したように、ネットワークソケットを開くことは、おそらく最も簡単で最もポータブルです。
使用して誰のためのwxPythonを自分のアプリケーションのために、あなたは、関数を使用することができ wx.SingleInstanceChecker 、ここで説明します。
私は個人的に、そのように実行されているアプリの既存のインスタンスがある場合にwx.Appそれを利用し、そこからwx.SingleInstanceChecker戻るサブクラスを使用します。FalseOnInit()
import wx
class SingleApp(wx.App):
"""
class that extends wx.App and only permits a single running instance.
"""
def OnInit(self):
"""
wx.App init function that returns False if the app is already running.
"""
self.name = "SingleApp-%s".format(wx.GetUserId())
self.instance = wx.SingleInstanceChecker(self.name)
if self.instance.IsAnotherRunning():
wx.MessageBox(
"An instance of the application is already running",
"Error",
wx.OK | wx.ICON_WARNING
)
return False
return True
これは、wx.App複数のインスタンスを禁止するための単純なドロップイン置換です。それを使用するにwx.AppはSingleApp、次のようにコードで単に置き換えます:
app = SingleApp(redirect=False)
frame = wx.Frame(None, wx.ID_ANY, "Hello World")
frame.Show(True)
app.MainLoop()
これが私の最終的なWindowsのみのソリューションです。以下をおそらく 'onlyone.py'などと呼ばれるモジュールに入れてください。そのモジュールを__メイン__ pythonスクリプトファイルに直接含めます。
import win32event, win32api, winerror, time, sys, os
main_path = os.path.abspath(sys.modules['__main__'].__file__).replace("\\", "/")
first = True
while True:
mutex = win32event.CreateMutex(None, False, main_path + "_{<paste YOUR GUID HERE>}")
if win32api.GetLastError() == 0:
break
win32api.CloseHandle(mutex)
if first:
print "Another instance of %s running, please wait for completion" % main_path
first = False
time.sleep(1)
コードは、スクリプトへの完全パスから派生した名前でミューテックスを作成しようとします。実際のファイルシステムとの混乱を避けるために、スラッシュを使用します。
Windowsでこれを行うための最善の解決策は、@ zgodaの提案に従ってミューテックスを使用することです。
import win32event
import win32api
from winerror import ERROR_ALREADY_EXISTS
mutex = win32event.CreateMutex(None, False, 'name')
last_error = win32api.GetLastError()
if last_error == ERROR_ALREADY_EXISTS:
print("App instance already running")
いくつかの答えは fctnlでは、Windowsでは使用できない(@sorin tendoパッケージにも含まれています)を使用しており、pyinstaller静的インポートを行うようなパッケージを使用してpythonアプリを凍結しようとすると、エラーがスローされます。
また、ファイルロックメソッドを使用するread-onlyと、データベースファイルに問題が発生します(でこれが発生しますsqlite3)。
私は新しいユーザーで、スタックオーバーフローではまだ投票できないので、これを回答として投稿します。
Sorin Sbarneaのソリューションは、OS X、Linux、およびWindowsで機能し、感謝しています。
ただし、tempfile.gettempdir()は、OS XとWindowsでは1つの方法で動作し、別のsome / many / all(?)* nixesでは動作します(OS XもUnixであるという事実を無視して!)。違いはこのコードにとって重要です。
OS XとWindowsにはユーザー固有の一時ディレクトリがあるため、あるユーザーが作成した一時ファイルは別のユーザーには表示されません。対照的に、* nixの多くのバージョン(私はUbuntu 9、RHEL 5、OpenSolaris 2008およびFreeBSD 8をテストしました)では、すべてのユーザーの一時ディレクトリは/ tmpです。
つまり、ロックファイルがマルチユーザーマシンで作成されると、/ tmpに作成され、最初にロックファイルを作成したユーザーだけがアプリケーションを実行できるようになります。
考えられる解決策は、ロックファイルの名前に現在のユーザー名を埋め込むことです。
ポートを取得するというOPのソリューションは、マルチユーザーマシンでも正しく動作しないことに注意してください。
私single_processはジェンツーで使用しています。
pip install single_process
例:
from single_process import single_process
@single_process
def main():
print 1
if __name__ == "__main__":
main()
ファイルシステムにアクセスすることなく、プロセスグループを使用する優れたPOSIXyソリューションがあるはずだと私は考え続けていますが、それを完全に特定することはできません。何かのようなもの:
起動時に、プロセスは特定のグループのすべてのプロセスに「kill -0」を送信します。そのようなプロセスが存在する場合は、終了します。その後、グループに参加します。他のプロセスはそのグループを使用しません。
ただし、これには競合状態があります。複数のプロセスがすべて同時にこれを実行し、最終的にすべてがグループに参加して同時に実行される可能性があります。何らかのmutexを追加して水密にするまでに、プロセスグループは不要になります。
これは、プロセスが1分ごとまたは1時間ごとに1回だけcronによって開始される場合は許容できるかもしれませんが、望まない日に正確に失敗するのではないかと少し不安になります。
誰かがそれを改善することができない限り、これは結局非常に良い解決策ではないと思いますか?
私は先週この正確な問題に遭遇し、いくつかの良い解決策を見つけましたが、非常にシンプルでクリーンなpythonパッケージを作成してPyPIにアップロードすることにしました。任意の文字列リソース名をロックできるという点で、tendoとは異なります。確かにロックできますが__file__同じ効果を得るためにする。
インストール: pip install quicklock
それを使用することは非常に簡単です:
[nate@Nates-MacBook-Pro-3 ~/live] python
Python 2.7.6 (default, Sep 9 2014, 15:04:36)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from quicklock import singleton
>>> # Let's create a lock so that only one instance of a script will run
...
>>> singleton('hello world')
>>>
>>> # Let's try to do that again, this should fail
...
>>> singleton('hello world')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/nate/live/gallery/env/lib/python2.7/site-packages/quicklock/quicklock.py", line 47, in singleton
raise RuntimeError('Resource <{}> is currently locked by <Process {}: "{}">'.format(resource, other_process.pid, other_process.name()))
RuntimeError: Resource <hello world> is currently locked by <Process 24801: "python">
>>>
>>> # But if we quit this process, we release the lock automatically
...
>>> ^D
[nate@Nates-MacBook-Pro-3 ~/live] python
Python 2.7.6 (default, Sep 9 2014, 15:04:36)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from quicklock import singleton
>>> singleton('hello world')
>>>
>>> # No exception was thrown, we own 'hello world'!
見てください:https : //pypi.python.org/pypi/quicklock
Roberto Rosarioの答えに基づいて、次の関数を思いつきました。
SOCKET = None
def run_single_instance(uniq_name):
try:
import socket
global SOCKET
SOCKET = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
## Create an abstract socket, by prefixing it with null.
# this relies on a feature only in linux, when current process quits, the
# socket will be deleted.
SOCKET.bind('\0' + uniq_name)
return True
except socket.error as e:
return False
グローバルを定義する必要があります SOCKET変数は、プロセス全体が終了したときにのみガベージコレクションされるため、。関数でローカル変数を宣言すると、関数の終了後にスコープから外れるため、ソケットが削除されます。
私は彼のコードを明確にし、詳しく説明するだけなので、すべてのクレジットはRoberto Rosarioに送られるべきです。そして、このコードは、https://troydhanson.github.io/network/Unix_domain_sockets.htmlからの以下の引用テキストが説明するように、Linuxでのみ機能します。
Linuxには特別な機能があります。UNIXドメインソケットのパス名がnullバイト\ 0で始まる場合、その名前はファイルシステムにマッピングされません。したがって、ファイルシステム内の他の名前と衝突することはありません。また、サーバーが抽象名前空間のUNIXドメインリスニングソケットを閉じると、そのファイルは削除されます。通常のUNIXドメインソケットでは、サーバーが閉じた後もファイルは保持されます。
Linuxの例
このメソッドは、アプリケーションを閉じると自動的に削除される一時ファイルの作成に基づいています。プログラムの起動により、ファイルの存在が確認されます。ファイルが存在する(実行が保留されている)場合、プログラムは閉じられます。それ以外の場合は、ファイルを作成してプログラムの実行を続行します。
from tempfile import *
import time
import os
import sys
f = NamedTemporaryFile( prefix='lock01_', delete=True) if not [f for f in os.listdir('/tmp') if f.find('lock01_')!=-1] else sys.exit()
YOUR CODE COMES HERE
Linuxシステムpgrep -aでは、インスタンスの数を尋ねることもでき
ます。スクリプトはプロセスリストにあります(オプション-aにより、完全なコマンドライン文字列が表示されます)。例えば
import os
import sys
import subprocess
procOut = subprocess.check_output( "/bin/pgrep -u $UID -a python", shell=True,
executable="/bin/bash", universal_newlines=True)
if procOut.count( os.path.basename(__file__)) > 1 :
sys.exit( ("found another instance of >{}<, quitting."
).format( os.path.basename(__file__)))
-u $UID制限をすべてのユーザーに適用する必要がある場合は削除します。免責事項:a)スクリプトの(ベース)名は一意であると想定されています。b)競合状態がある可能性があります。
import sys,os
# start program
try: # (1)
os.unlink('lock') # (2)
fd=os.open("lock", os.O_CREAT|os.O_EXCL) # (3)
except:
try: fd=os.open("lock", os.O_CREAT|os.O_EXCL) # (4)
except:
print "Another Program running !.." # (5)
sys.exit()
# your program ...
# ...
# exit program
try: os.close(fd) # (6)
except: pass
try: os.unlink('lock')
except: pass
sys.exit()