सुनिश्चित करें कि प्रोग्राम का केवल एक ही उदाहरण चल रहा है


120

क्या पाइथोनिक तरीके से किसी प्रोग्राम का केवल एक उदाहरण चल रहा है?

एकमात्र उचित समाधान जो मैं लेकर आया हूं, वह इसे कुछ पोर्ट पर सर्वर के रूप में चलाने की कोशिश कर रहा है, फिर दूसरे प्रोग्राम को उसी पोर्ट पर बांधने की कोशिश कर रहा है - विफल। लेकिन यह वास्तव में एक महान विचार नहीं है, हो सकता है कि इससे कुछ अधिक हल्का हो?

(ध्यान रखें कि कार्यक्रम कभी-कभी विफल होने की उम्मीद है, यानी segfault - इसलिए "लॉक फाइल" जैसी चीजें काम नहीं करेंगी)


1
शायद आपकी ज़िंदगी आसान हो जाती अगर आप नीचे ट्रैक करते और सेगफ़ॉल्ट तय करते। ऐसा नहीं है कि यह एक आसान बात है।
डेविड लॉक

यह मेरी लाइब्रेरी में नहीं है, यह अजगर के लिबक्स एमएल बाइंडिंग और बेहद शर्मीले हैं - एक दो दिनों में केवल एक बार आग लगती है।
स्लाव वी

5
पायथन की मानक लाइब्रेरी झुंड () का समर्थन करती है, जो आधुनिक यूनिक्स कार्यक्रमों के लिए राइट थिंग है। पोर्ट खोलना एक अधिक विवश नामस्थान में एक स्पॉट का उपयोग करता है, जबकि pidfiles अधिक जटिल हैं क्योंकि आपको उन्हें सुरक्षित रूप से अमान्य करने के लिए चल रही प्रक्रियाओं की जांच करने की आवश्यकता है; झुंड में न तो समस्या है।
चार्ल्स डफी

s / UNIX / linux / वहाँ तुम जाओ, FTFY।
कलीसिन

जवाबों:


100

निम्नलिखित कोड को काम करना चाहिए, यह क्रॉस-प्लेटफ़ॉर्म है और पायथन 2.4-3.2 पर चलता है। मैंने इसे विंडोज, ओएस एक्स और लिनक्स पर परीक्षण किया।

from tendo import singleton
me = singleton.SingleInstance() # will sys.exit(-1) if other instance is running

नवीनतम कोड संस्करण singleton.py उपलब्ध है । कृपया यहां बग दर्ज करें

आप निम्न विधियों में से एक का उपयोग कर स्थापित कर सकते हैं:

  • easy_install tendo
  • pip install tendo
  • मैन्युअल रूप से इसे http://pypi.python.org/pypi/tendo से प्राप्त करके

2
मैंने उत्तर को अपडेट किया और नवीनतम संस्करण के लिए एक लिंक शामिल किया। यदि आपको कोई बग मिलता है तो कृपया इसे गितुब में सबमिट करें और मैं इसे जल्द से जल्द हल कर दूंगा।
सोरिन

2
@ जॉनी_म धन्यवाद, मैंने एक पैच बनाया और एक नया संस्करण pypi.python.org/pypi/tendo
sorin

2
पायथन 2.6 के तहत यह सिंटैक्स मेरे लिए विंडोज़ पर काम नहीं करता था। मेरे लिए क्या काम किया गया था: 1: तेंदूपत्ता आयात एकल 2 से: मुझे = singleton.SingleInstance ()
ब्रायन

25
इस के रूप में तुच्छ के रूप में कुछ के लिए एक और निर्भरता? बहुत आकर्षक नहीं लगता।
WhyNotHugo

2
क्या सिंगलटन उन प्रक्रियाओं को संभालता है जिन्हें एक सिग्मेट मिलता है (उदाहरण के लिए यदि कोई प्रक्रिया बहुत लंबी चल रही है), या क्या मुझे इसे संभालना है?
जिमजेटी

43

सरल, क्रॉस-प्लेटफ़ॉर्म समाधान, ज़गोड़ा द्वारा एक और प्रश्न में पाया गया :

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

एस.लॉट के सुझाव की तरह, लेकिन कोड के साथ।


जिज्ञासा से बाहर: यह वास्तव में क्रॉस-प्लेटफॉर्म है? क्या यह विंडोज पर काम करता है?
जोआचिम सॉउर

1
fcntlविंडोज पर कोई मॉड्यूल नहीं है (हालांकि कार्यक्षमता का अनुकरण किया जा सकता है)।
jfs

10
TIP: यदि आप इसे किसी फ़ंक्शन में लपेटना चाहते हैं, तो 'fp' वैश्विक होना चाहिए या फ़ंक्शन के बाहर निकलने के बाद फ़ाइल बंद हो जाएगी।
cmcginty

1
@ मिरको कंट्रोल + जेड एक एप्लिकेशन से बाहर नहीं निकलता है (किसी भी ओएस पर जो मुझे पता है), यह इसे निलंबित करता है। आवेदन के साथ अग्रभूमि में वापस आ सकता है fg। तो, ऐसा लगता है कि यह आपके लिए सही तरीके से काम कर रहा है (यानी ऐप अभी भी सक्रिय है, लेकिन निलंबित है, इसलिए लॉक जगह पर है)।
सैम बुल्

1
मेरी स्थिति में इस कोड (लिनक्स पर पायथन 3.8.3) को मॉडिफिकेशन की आवश्यकता थी:lock_file_pointer = os.open(lock_path, os.O_WRONLY | os.O_CREAT)
बाज़ोरेक

30

यह कोड लिनक्स विशिष्ट है। यह 'अमूर्त' UNIX डोमेन सॉकेट का उपयोग करता है, लेकिन यह सरल है और बासी लॉक फ़ाइलों को इधर-उधर नहीं छोड़ेगा। मैं इसे ऊपर के समाधान के लिए पसंद करता हूं क्योंकि इसे विशेष रूप से आरक्षित टीसीपी पोर्ट की आवश्यकता नहीं है।

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कई प्रोग्रामों को लागू करने के लिए अद्वितीय स्ट्रिंग को बदला जा सकता है, जिसे लागू करने की आवश्यकता है।


1
रॉबर्टो, क्या आप सुनिश्चित हैं कि कर्नेल घबराहट या हार्ड रीसेट के बाद, फ़ाइल \ 0postconnect_gateway_notify_lock बूट अप पर मौजूद नहीं होगा? मेरे मामले में AF_UNIX सॉकेट फ़ाइल इसके बाद भी मौजूद है और यह पूरे विचार को नष्ट कर देती है। विशिष्ट फ़ाइल नाम पर लॉक प्राप्त करने के साथ ऊपर समाधान इस मामले में बहुत विश्वसनीय है।
डेनियल गुरियनोव

2
जैसा कि ऊपर उल्लेख किया गया है, यह समाधान लिनक्स पर काम करता है, लेकिन मैक ओएस एक्स पर नहीं।
बिलाल और ओल्गा

2
यह समाधान काम नहीं करता है। मैंने इसे Ubuntu 14.04 पर आज़माया। एक ही स्क्रिप्ट को 2 टर्मिनल विंडो से एक साथ चलाएं। वे दोनों ही ठीक चल रहे हैं।
डिमोन

1
इसने मेरे लिए उबंटू 16 में काम किया। और किसी भी तरह से इस प्रक्रिया को मारना एक दूसरे को शुरू करने की अनुमति देता है। डिमन मुझे लगता है कि आपने अपने परीक्षण में कुछ गलत किया है। (शायद आप उपरोक्त कोड के चलने के बाद अपनी स्क्रिप्ट को बनाना भूल गए, इसलिए यह तुरंत बाहर निकल गया और सॉकेट जारी किया।)
लूका

1
यह सोने का सवाल नहीं है। कोड काम करता है लेकिन केवल इनलाइन कोड के रूप में। मैं इसे एक समारोह में डाल रहा था। फ़ंक्शन मौजूद होते ही सॉकेट गायब हो रहा था।
स्टीव कोहेन

25

मुझे नहीं पता कि यह काफी पाइथोनिक है, लेकिन जावा वर्ल्ड में एक परिभाषित पोर्ट पर सुनना एक बहुत व्यापक रूप से उपयोग किया जाने वाला समाधान है, क्योंकि यह सभी प्रमुख प्लेटफार्मों पर काम करता है और दुर्घटनाग्रस्त कार्यक्रमों के साथ कोई समस्या नहीं है।

पोर्ट को सुनने का एक और फायदा यह है कि आप रनिंग इंस्टेंस पर कमांड भेज सकते हैं। उदाहरण के लिए जब उपयोगकर्ता प्रोग्राम को दूसरी बार शुरू करते हैं, तो आप एक अन्य विंडो खोलने के लिए यह बताने के लिए रनिंग इंस्टेंस भेज सकते हैं (उदाहरण के लिए फ़ायरफ़ॉक्स यही करता है। मुझे नहीं पता कि वे टीसीपी पोर्ट या नामित पाइप का उपयोग करते हैं या नहीं। ऐसा कुछ, 'हालांकि)।


+1 यह विशेष रूप से, क्योंकि यह मुझे रनिंग इंस्टेंस को सूचित करने की अनुमति देता है, इसलिए यह एक और विंडो बनाता है, पॉप अप करता है, आदि
WhyNotHugo

1
उदाहरण के लिए उपयोग करें import socket; s = socket.socket(socket.AF_INET, socket.SOCK_STREAM); s.bind(('localhost', DEFINED_PORT))। एक OSErrorअगर किसी अन्य प्रक्रिया एक ही बंदरगाह के लिए बाध्य है बढ़ा दी जाएगी।
crishoj

13

पहले कभी भी अजगर नहीं लिखा, लेकिन यह वही है जो मैंने अभी-अभी mycheckpoint में लागू किया है, इसे रोकने के लिए इसे crond द्वारा दो या अधिक बार शुरू किया गया है:

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) में इसे पोस्ट करने के बाद स्लावा-एन का सुझाव मिला। यह एक फ़ंक्शन के रूप में कहा जाता है, निष्पादित स्क्रिप्ट फ़ाइल (एक पीआईडी ​​फ़ाइल नहीं) को लॉक करता है और स्क्रिप्ट समाप्त होने (सामान्य या त्रुटि) तक लॉक को बनाए रखता है।


1
बहुत खूबसूरत। मैंने इसे बदल दिया इसलिए इसे स्क्रिप्ट के तर्कों से रास्ता मिल गया। साथ ही इसे कहीं आम में एम्बेड करने की सलाह देते हैं - उदाहरण
जोसेफ हारुष

10

एक पीआईडी ​​फ़ाइल का उपयोग करें। आपके पास कुछ ज्ञात स्थान हैं, "/ पथ / से / 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())

तो, दूसरे शब्दों में, आप जाँच कर रहे हैं कि क्या एक पिडफाइल मौजूद है; यदि नहीं, तो उस फाइल पर अपना पीआईडी ​​लिखें। यदि पिडफाइल मौजूद है, तो यह देखने के लिए जांचें कि क्या पिड एक चलने वाली प्रक्रिया का पिड है; यदि ऐसा है, तो आपको एक और लाइव प्रक्रिया चल रही है, इसलिए बस बंद कर दें। यदि नहीं, तो पिछली प्रक्रिया दुर्घटनाग्रस्त हो गई थी, इसलिए इसे लॉग इन करें, और फिर पुराने के स्थान पर फ़ाइल में अपना स्वयं का पीआईडी ​​लिखें। फिर जारी रखें।


4
यह एक दौड़ की स्थिति है। परीक्षण-तत्कालीन लेखन अनुक्रम दो कार्यक्रमों को छोड़कर एक साथ शुरू हो सकता है, कोई फ़ाइल नहीं मिल सकती है और समवर्ती लिखने के लिए खोलने का प्रयास करें। इसे एक पर एक अपवाद को उठाना चाहिए , जिससे दूसरे को आगे बढ़ना चाहिए।
S.Lott

6

आपको पहले से ही एक और प्रश्न में इसी तरह के प्रश्न का उत्तर मिला है, इसलिए पूर्णता के लिए देखें कि विंडोज म्यूटेक्स नाम के एक ही पर कैसे प्राप्त किया जाए।

http://code.activestate.com/recipes/474070/


5

यह काम कर सकता है।

  1. किसी ज्ञात स्थान पर PID फ़ाइल बनाने का प्रयास करें। यदि आप असफल होते हैं, तो किसी के पास फ़ाइल बंद है, आपका काम हो गया

  2. जब आप सामान्य रूप से समाप्त करते हैं, पीआईडी ​​फ़ाइल को बंद करें और निकालें, तो कोई और इसे अधिलेखित कर सकता है।

आप अपने प्रोग्राम को शेल स्क्रिप्ट में लपेट सकते हैं जो PID फ़ाइल को हटा देता है भले ही आपका प्रोग्राम क्रैश हो जाए।

यदि आप हैंग भी करते हैं, तो प्रोग्राम को मारने के लिए पीआईडी ​​फ़ाइल का उपयोग कर सकते हैं।


3

लॉक-फ़ाइल का उपयोग करना यूनिक्स पर एक बहुत ही सामान्य दृष्टिकोण है। यदि यह क्रैश हो जाता है, तो आपको मैन्युअल रूप से सफाई करनी होगी। आप पीआईडी ​​को फाइल में स्टोर कर सकते हैं, और अगर इस पीआईडी ​​के साथ कोई प्रक्रिया है तो स्टार्टअप चेक पर, लॉक-फाइल को ओवरराइड करते हुए यदि नहीं। (हालांकि, आपको रीड-फाइल-चेक-पिड-रीराइट-फाइल के चारों ओर लॉक की भी आवश्यकता है)। आप आप में हो रही है और पीआईडी की जाँच के लिए क्या जरूरत है मिलेगा ओएस -package। जाँच करने का सामान्य तरीका अगर किसी दिए गए पीड के साथ कोई प्रक्रिया मौजूद है, तो यह एक गैर-घातक संकेत भेजना है।

अन्य विकल्प इसे झुंड या पॉज़िक्स सेमाफोर के साथ जोड़ सकते हैं।

एक नेटवर्क सॉकेट खोलना, जैसा कि प्रस्तावित किया गया है, संभवतः सबसे आसान और सबसे पोर्टेबल होगा।


3

अपने आवेदन के लिए wxPython का उपयोग करने वाले किसी भी व्यक्ति के लिए, आप wx.SingleInstanceChecker यहाँ प्रलेखित फ़ंक्शन का उपयोग कर सकते हैं

मैं व्यक्तिगत रूप से एक उपवर्ग का उपयोग wx.Appजो का उपयोग करता है wx.SingleInstanceCheckerरिटर्न और Falseसे OnInit()अगर कोई एप्लिकेशन पहले से ही ऐसा तरह क्रियान्वित करने के लिए एक मौजूदा उदाहरण है:

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()

एक सिंगलटन के लिए सॉकेट-लिस्टिंग थ्रेड को कोड करने के बाद मुझे यह मिला, जो बहुत अच्छा काम करता है और मैंने पहले से ही एक युगल कार्यक्रम में स्थापित किया है, हालांकि, मुझे अतिरिक्त "वेकअप" चाहिए, मैं सिंगलटन दे सकता हूं ताकि मैं इसे ला सकूं ओवरलैपिंग खिड़कियों के एक बड़े ढेर के सामने और केंद्र। इसके अलावा: "यहाँ पर प्रलेखित" लिंक बहुत बेकार ऑटो-जनरेटेड डॉक्यूमेंटेशन की ओर
इशारा

@RufusVS आप सही हैं - यह बहुत बेहतर प्रलेखन लिंक है, ने उत्तर को अपडेट किया है।
मैट कूब्र्ट सेप

3

यहां मेरा अंतिम विंडोज-केवल समाधान है। निम्नलिखित को एक मॉड्यूल में रखें, जिसे 'onlyone.py', या जो भी हो। उस मॉड्यूल को सीधे अपने __ मुख्य __ पायथन स्क्रिप्ट फ़ाइल में शामिल करें।

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)

व्याख्या

कोड स्क्रिप्ट के लिए पूर्ण पथ से व्युत्पन्न नाम के साथ एक म्यूटेक्स बनाने का प्रयास करता है। हम वास्तविक फ़ाइल सिस्टम के साथ संभावित भ्रम से बचने के लिए फॉरवर्ड-स्लैश का उपयोग करते हैं।

लाभ

  • किसी कॉन्फ़िगरेशन या 'मैजिक' आइडेंटिफ़ायर की ज़रूरत नहीं है, इसे ज़रूरत के अनुसार कई अलग-अलग स्क्रिप्ट में उपयोग करें।
  • कोई भी बासी फाइल आसपास नहीं छोड़ी जाती है, म्यूटेक्स आपके साथ मर जाता है।
  • प्रतीक्षा करते समय एक सहायक संदेश प्रिंट करता है

3

विंडोज़ पर इसके लिए सबसे अच्छा समाधान है @ ज़गोडा द्वारा सुझाए गए म्यूटेक्स का उपयोग करना।

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(यह भी @sorin tendo पैकेज में शामिल है) जो खिड़कियों पर उपलब्ध नहीं है और क्या आपको अपने अजगर ऐप को एक पैकेज का उपयोग करके फ्रीज करने का प्रयास करना चाहिए जैसे pyinstallerकि स्थिर आयात करता है, यह एक त्रुटि फेंकता है।

इसके अलावा, लॉक फ़ाइल विधि का उपयोग करते हुए, read-onlyडेटाबेस फ़ाइलों (इसके साथ अनुभव किया गया sqlite3) के साथ एक समस्या पैदा होती है ।


2

मैं इसे उत्तर के रूप में पोस्ट कर रहा हूं क्योंकि मैं एक नया उपयोगकर्ता हूं और स्टैक ओवरफ्लो मुझे अभी तक वोट नहीं करने देगा।

सोरिन सर्बनेया का समाधान ओएस एक्स, लिनक्स और विंडोज के तहत मेरे लिए काम करता है, और मैं इसके लिए आभारी हूं।

हालाँकि, tempfile.gettempdir () OS X और Windows के तहत एक तरह से व्यवहार करता है और दूसरा अन्य कुछ / कई / सभी (?) * Nixes (इस तथ्य की अनदेखी करते हुए कि OS X भी Unix है!)। अंतर इस कोड के लिए महत्वपूर्ण है।

OS X और Windows में उपयोगकर्ता-विशिष्ट अस्थायी निर्देशिकाएं होती हैं, इसलिए एक उपयोगकर्ता द्वारा बनाया गया एक tempfile दूसरे उपयोगकर्ता को दिखाई नहीं देता है। इसके विपरीत, * nix के कई संस्करणों के अंतर्गत (मैंने Ubuntu 9, RHEL 5, OpenSolaris 2008 और FreeBSD 8 का परीक्षण किया), सभी उपयोगकर्ताओं के लिए अस्थायी dir / tmp है।

इसका मतलब है कि जब मल्टी-यूज़र मशीन पर लॉकफ़ाइल बनाया जाता है, तो इसे / tmp में बनाया जाता है और केवल पहली बार लॉकफ़ाइल बनाने वाले उपयोगकर्ता ही एप्लिकेशन चला पाएंगे।

एक संभावित समाधान लॉक फ़ाइल के नाम से वर्तमान उपयोगकर्ता नाम को एम्बेड करना है।

यह ध्यान देने योग्य है कि पोर्ट को हथियाने का ओपी का समाधान एक बहु-उपयोगकर्ता मशीन पर भी दुर्व्यवहार करेगा।


कुछ पाठकों के लिए (उदाहरण के लिए) वांछित व्यवहार यह है कि केवल एक प्रति अवधि चल सकती है, भले ही कितने उपयोगकर्ता शामिल हों। तो प्रति-उपयोगकर्ता tmp निर्देशिकाएं टूटी हुई हैं, जबकि साझा / tmp या पोर्ट लॉक वांछित व्यवहार प्रदर्शित करता है।
जोनाथन हार्टले

2

मैं single_processअपने gentoo पर उपयोग करता हूं;

pip install single_process

उदाहरण :

from single_process import single_process

@single_process
def main():
    print 1

if __name__ == "__main__":
    main()   

देखें: https://pypi.python.org/pypi/single_process/1.0


Py3 में विफल रहता है। पैकेज गलत लगता है।
एकेवू सेप

विंडोज पर मुझे मिलता है: ImportError: fcntl नाम का कोई मॉड्यूल नहीं
एंड्रयू डब्ल्यू फिलिप्स

1

मुझे संदेह है कि वहाँ एक अच्छा POSIXy प्रक्रिया समूहों का उपयोग कर समाधान होना चाहिए, फ़ाइल सिस्टम को हिट किए बिना, लेकिन मैं इसे बहुत कम नहीं कर सकता। कुछ इस तरह:

स्टार्टअप पर, आपकी प्रक्रिया किसी विशेष समूह में सभी प्रक्रियाओं को 'मार -0' भेजती है। यदि ऐसी कोई प्रक्रिया मौजूद है, तो यह बाहर निकल जाती है। फिर यह समूह में शामिल हो जाता है। कोई अन्य प्रक्रिया उस समूह का उपयोग नहीं करती है।

हालांकि, इसकी एक दौड़ की स्थिति है - कई प्रक्रियाएं एक ही समय में यह सब कर सकती हैं और सभी अंत में समूह में शामिल हो सकते हैं और एक साथ चल सकते हैं। जब तक आप इसे वाटरटाइट बनाने के लिए कुछ प्रकार के म्यूटेक्स जोड़ चुके हैं, तब तक आपको प्रक्रिया समूहों की आवश्यकता नहीं है।

यह स्वीकार्य हो सकता है यदि आपकी प्रक्रिया केवल क्रोन द्वारा शुरू होती है, हर मिनट या हर घंटे में एक बार, लेकिन यह मुझे थोड़ा परेशान करता है कि यह उस दिन ठीक से गलत हो जाएगा जब आप इसे नहीं चाहते हैं।

मुझे लगता है कि यह सब के बाद एक बहुत अच्छा समाधान नहीं है, जब तक कि कोई उस पर सुधार न कर सके?


1

मैं पिछले हफ्ते इस सटीक समस्या में भाग गया, और हालांकि मुझे कुछ अच्छे समाधान मिले, मैंने एक बहुत ही सरल और स्वच्छ अजगर पैकेज बनाने का फैसला किया और इसे PyPI पर अपलोड किया। यह टेंडो से भिन्न है कि यह किसी भी स्ट्रिंग संसाधन नाम को लॉक कर सकता है। हालांकि आप निश्चित रूप __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


1
मैंने इसे केवल "पाइप इंस्टॉल क्विकलॉक" के माध्यम से स्थापित किया है, लेकिन जब मैं इसे "क्विकलॉक आयात सिंगलटन से" के माध्यम से उपयोग करने का प्रयास करता हूं तो मुझे एक अपवाद मिलता है: "इंपोर्टर:" नाम 'सिंगलटन' आयात नहीं कर सकता। यह एक मैक पर है।
ग्रेवाई

यह पता चला है कि प्लॉक अजगर के साथ काम नहीं करता है। यही कारण है कि यह मेरे लिए असफल रहा।
ग्रेईई

हाँ, क्षमा करें, यह भविष्य में प्रमाणित नहीं था। मैं इसे काम दिलाने में योगदान का स्वागत करूंगा!
नैट फेरेरो

1

रॉबर्टो रोसारियो के जवाब पर बिल्डिंग, मैं निम्नलिखित समारोह के साथ आता हूं:

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परिवर्तन को परिभाषित करने की आवश्यकता है क्योंकि यह केवल कचरा एकत्र किया जाएगा जब पूरी प्रक्रिया समाप्त हो जाएगी। यदि हम फ़ंक्शन में एक स्थानीय चर घोषित करते हैं, तो फ़ंक्शन के बाहर निकलने के बाद यह गुंजाइश से बाहर हो जाएगा, इस प्रकार सॉकेट को हटा दिया जाएगा।

सारा श्रेय रॉबर्टो रोसारियो को जाना चाहिए, क्योंकि मैं केवल उनके कोड को स्पष्ट और विस्तृत करता हूं। और यह कोड केवल लिनक्स पर काम करेगा, जैसा कि https://troydhanson.github.io/network/Unix_domain_sockets.html के निम्न उद्धृत पाठ में बताया गया है:

लिनक्स में एक विशेष विशेषता है: यदि UNIX डोमेन सॉकेट के लिए पथनाम एक शून्य बाइट \ 0 से शुरू होता है, तो इसका नाम फाइलसिस्टम में मैप नहीं किया गया है। इस प्रकार यह फाइलसिस्टम में अन्य नामों से नहीं टकराएगा। इसके अलावा, जब कोई सर्वर अमूर्त नेमस्पेस में अपने यूनिक्स डोमेन सुनने वाले सॉकेट को बंद कर देता है, तो उसकी फाइल डिलीट हो जाती है; नियमित UNIX डोमेन सॉकेट के साथ, फ़ाइल सर्वर के बंद होने के बाद भी बनी रहती है।


0

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

1
ढेर अतिप्रवाह में आपका स्वागत है! हालांकि यह उत्तर सही हो सकता है, कृपया कुछ स्पष्टीकरण जोड़ें। अंतर्निहित तर्क को लागू करना केवल कोड देने से अधिक महत्वपूर्ण है, क्योंकि यह ओपी और अन्य पाठकों को इसे और इसी तरह के मुद्दों को स्वयं ठीक करने में मदद करता है।
CodeMouse92

क्या यह सूत्र है? चेक और अस्थायी फ़ाइल निर्माण की तरह लगता है परमाणु नहीं कर रहे हैं ...
16:17 पर coppit

0

लिनक्स सिस्टम पर कोई भी 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यदि प्रतिबंध सभी उपयोगकर्ताओं पर लागू होना चाहिए । अस्वीकरण: ए) यह माना जाता है कि स्क्रिप्ट का (आधार) नाम अद्वितीय है, बी) दौड़ की स्थिति हो सकती है।


-1
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()  

2
ढेर अतिप्रवाह में आपका स्वागत है! हालांकि यह कोड ब्लॉक प्रश्न का उत्तर दे सकता है, यह सबसे अच्छा होगा यदि आप ऐसा क्यों करते हैं इसके लिए थोड़ा स्पष्टीकरण प्रदान कर सकते हैं। कृपया इस तरह के विवरण को शामिल करने के लिए अपने उत्तर को संपादित करें।
आर्टजोम बी।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.