Đảm bảo chỉ một phiên bản chương trình đang chạy


120

Có cách nào của Pythonic để chỉ có một phiên bản của chương trình đang chạy không?

Giải pháp hợp lý duy nhất mà tôi đưa ra là cố gắng chạy nó như một máy chủ trên một số cổng, sau đó chương trình thứ hai cố gắng liên kết với cùng một cổng - không thành công. Nhưng nó không thực sự là một ý tưởng tuyệt vời, có thể có thứ gì đó nhẹ hơn thế này?

(Hãy xem xét rằng chương trình dự kiến ​​sẽ bị lỗi đôi khi, tức là segfault - vì vậy những thứ như "tệp khóa" sẽ không hoạt động)


1
Có lẽ cuộc sống của bạn sẽ dễ dàng hơn nếu bạn theo dõi và sửa lỗi segfault. Đó không phải là một điều dễ dàng để làm.
David Locke

Nó không có trong thư viện của tôi, nó nằm trong các liên kết libxml của python và cực kỳ nhút nhát - chỉ kích hoạt một lần một vài ngày.
Slava V

5
Thư viện chuẩn của Python hỗ trợ bầy đàn (), là Điều đúng đắn cho các chương trình UNIX hiện đại. Việc mở một cổng sử dụng một vị trí trong không gian tên hạn chế hơn nhiều, trong khi các tệp pidfiles phức tạp hơn vì bạn cần kiểm tra các quy trình đang chạy để làm mất hiệu lực chúng một cách an toàn; đàn không có vấn đề.
Charles Duffy

s / UNIX / linux / đó bạn, FTFY.
kaleissin

Câu trả lời:


100

Đoạn mã sau sẽ thực hiện công việc, nó đa nền tảng và chạy trên Python 2.4-3.2. Tôi đã thử nghiệm nó trên Windows, OS X và Linux.

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

Phiên bản mã mới nhất có sẵn singleton.py . Vui lòng gửi lỗi ở đây .

Bạn có thể cài đặt xu hướng bằng một trong các phương pháp sau:


2
Tôi đã cập nhật câu trả lời và bao gồm một liên kết đến phiên bản mới nhất. Nếu bạn tìm thấy lỗi, vui lòng gửi nó đến github và tôi sẽ giải quyết nó trong thời gian sớm nhất.
sorin

2
@Johny_M Cảm ơn, tôi làm một bản vá và phát hành một phiên bản mới hơn trên pypi.python.org/pypi/tendo
Sorin

2
Cú pháp này không hoạt động đối với tôi trên các cửa sổ trong Python 2.6. Điều làm việc cho tôi là: 1: from tenso import singleton 2: me = singleton.SingleInstance ()
Brian

25
Một sự phụ thuộc khác cho một cái gì đó tầm thường như thế này? Nghe không hấp dẫn lắm.
WhyNotHugo

2
Singleton có xử lý các quy trình nhận được một ký hiệu (ví dụ: nếu một quy trình đang chạy quá lâu) hay tôi phải xử lý điều đó?
JimJty

43

Giải pháp đơn giản, đa nền tảng , được tìm thấy trong một câu hỏi khác của zgoda :

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

Rất giống gợi ý của S.Lott, nhưng với mã.


Vì tò mò: đây có thực sự là nền tảng đa nền tảng? Nó có hoạt động trên Windows không?
Joachim Sauer 21/12/08

1
Không có fcntlmô-đun nào trên Windows (mặc dù chức năng có thể được mô phỏng).
jfs 21/12/08

10
MẸO: nếu bạn muốn gói nó trong một hàm 'fp' phải là toàn cục hoặc tệp sẽ bị đóng sau khi hàm thoát.
cmcginty

1
@Mirko Control + Z không thoát khỏi ứng dụng (trên bất kỳ hệ điều hành nào mà tôi biết), nó sẽ tạm ngưng. Ứng dụng có thể được quay trở lại nền trước với fg. Vì vậy, có vẻ như nó đang hoạt động bình thường đối với bạn (tức là ứng dụng vẫn hoạt động, nhưng bị treo, vì vậy khóa vẫn ở nguyên vị trí).
Sam Bull

1
Mã này trong tình huống của tôi (Python 3.8.3 trên Linux) cần được sửa đổi:lock_file_pointer = os.open(lock_path, os.O_WRONLY | os.O_CREAT)
baziorek

30

Mã này dành riêng cho Linux. Nó sử dụng các ổ cắm miền UNIX 'trừu tượng', nhưng nó đơn giản và sẽ không để lại các tệp khóa cũ xung quanh. Tôi thích nó hơn giải pháp ở trên vì nó không yêu cầu một cổng TCP dành riêng.

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) 

Chuỗi duy nhất postconnect_gateway_notify_lockcó thể được thay đổi để cho phép nhiều chương trình cần một phiên bản duy nhất được thực thi.


1
Roberto, bạn có chắc rằng sau khi khởi động lại kernel hoặc hard reset, tệp \ 0postconnect_gateway_notify_lock sẽ không xuất hiện khi khởi động không? Trong trường hợp của tôi, tệp socket AF_UNIX vẫn hiện diện sau đó và điều này phá hủy toàn bộ ý tưởng. Giải pháp ở trên với việc mua lại khóa trên tên tệp cụ thể là đáng tin cậy trong trường hợp này.
Danylo Gurianov

2
Như đã lưu ý ở trên, giải pháp này hoạt động trên Linux nhưng không hoạt động trên Mac OS X.
Bilal và Olga

2
Giải pháp này không hoạt động. Tôi đã thử nó trên Ubuntu 14.04. Chạy cùng một tập lệnh từ 2 cửa sổ đầu cuối cùng một lúc. Cả hai đều chạy tốt.
Dimon

1
Điều này đã làm việc cho tôi trong Ubuntu 16. Và việc giết quá trình bằng bất kỳ cách nào cho phép một quá trình khác bắt đầu. Dimon Tôi nghĩ bạn đã làm sai điều gì đó trong bài kiểm tra của bạn. (Có lẽ bạn đã quên đặt tập lệnh của mình ở chế độ ngủ sau khi mã trên chạy, vì vậy nó ngay lập tức thoát và giải phóng ổ cắm.)
Luke

1
Đó không phải là vấn đề về giấc ngủ. Mã hoạt động nhưng chỉ như mã nội tuyến. Tôi đã đưa nó vào một chức năng. Ổ cắm đã biến mất ngay khi chức năng tồn tại.
Steve Cohen

25

Tôi không biết liệu nó có đủ khó hiểu hay không, nhưng trong thế giới Java, lắng nghe trên một cổng xác định là một giải pháp được sử dụng khá rộng rãi, vì nó hoạt động trên tất cả các nền tảng chính và không gặp bất kỳ vấn đề nào với các chương trình bị treo.

Một lợi thế khác của việc lắng nghe một cổng là bạn có thể gửi một lệnh đến phiên bản đang chạy. Ví dụ: khi người dùng khởi động chương trình lần thứ hai, bạn có thể gửi cho phiên bản đang chạy một lệnh để yêu cầu nó mở một cửa sổ khác (ví dụ như đó là những gì Firefox làm. Tôi không biết liệu họ có sử dụng cổng TCP hay các đường ống được đặt tên hay không một cái gì đó tương tự, 'mặc dù).


+1 đến nay, đặc biệt vì nó cho phép tôi để thông báo cho các trường hợp chạy, vì vậy nó tạo ra một cửa sổ khác, bật lên, vv
WhyNotHugo

1
Sử dụng vd import socket; s = socket.socket(socket.AF_INET, socket.SOCK_STREAM); s.bind(('localhost', DEFINED_PORT)). An OSErrorsẽ được nâng lên nếu một tiến trình khác được liên kết với cùng một cổng.
khủng hoảng

13

Chưa bao giờ viết python trước đây, nhưng đây là những gì tôi vừa triển khai trong mycheckpoint, để ngăn nó được khởi động hai lần hoặc nhiều hơn bởi 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()

Tìm thấy đề xuất của Slava-N sau khi đăng đề xuất này trong một số báo khác (http://stackoverflow.com/questions/2959474). Cái này được gọi là một hàm, khóa tệp kịch bản đang thực thi (không phải tệp pid) và duy trì khóa cho đến khi tập lệnh kết thúc (bình thường hoặc lỗi).


1
Rất thanh lịch. Tôi đã thay đổi nó để nó lấy đường dẫn từ các đối số của script. Cũng khuyên bạn nên nhúng cái này vào một nơi nào đó phổ biến - Ví dụ
Jossef Harush

10

Sử dụng tệp pid. Bạn có một số vị trí đã biết, "/ path / to / pidfile" và khi khởi động, bạn làm điều gì đó như thế này (một phần là mã giả vì tôi là người chuẩn bị cà phê và không muốn làm việc chăm chỉ):

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

Vì vậy, nói cách khác, bạn đang kiểm tra xem pidfile có tồn tại hay không; nếu không, hãy ghi pid của bạn vào tệp đó. Nếu pidfile tồn tại, hãy kiểm tra xem pid có phải là pid của một tiến trình đang chạy hay không; nếu vậy, thì bạn có một tiến trình trực tiếp khác đang chạy, vì vậy chỉ cần tắt. Nếu không, thì quá trình trước đó đã bị lỗi, vì vậy hãy ghi lại nó, sau đó ghi pid của riêng bạn vào tệp thay cho tệp cũ. Sau đó tiếp tục.


4
Điều này có một điều kiện chủng tộc. Trình tự kiểm tra sau đó ghi có thể dẫn đến một ngoại lệ là hai chương trình khởi động gần như đồng thời, không tìm thấy tệp nào và cố gắng mở để ghi đồng thời. Nó sẽ đưa ra một ngoại lệ trên một cái, cho phép cái kia tiếp tục.
S.Lott

6

Bạn đã tìm thấy câu trả lời cho câu hỏi tương tự trong một chuỗi khác, vì vậy, vì lợi ích của sự đầy đủ, hãy xem cách đạt được điều tương tự trên Windows uning có tên mutex.

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


5

Điều này có thể hoạt động.

  1. Cố gắng tạo tệp PID đến một vị trí đã biết. Nếu bạn thất bại, ai đó đã khóa tệp, bạn đã hoàn tất.

  2. Khi bạn hoàn tất bình thường, hãy đóng và xóa tệp PID để người khác có thể ghi đè lên.

Bạn có thể bọc chương trình của mình trong một tập lệnh shell loại bỏ tệp PID ngay cả khi chương trình của bạn bị lỗi.

Bạn cũng có thể sử dụng tệp PID để giết chương trình nếu nó bị treo.


3

Sử dụng tệp khóa là một cách tiếp cận khá phổ biến trên unix. Nếu nó bị treo, bạn phải dọn dẹp thủ công. Bạn có thể đánh dấu PID trong tệp và khi khởi động, hãy kiểm tra xem có quá trình nào xảy ra với PID này hay không, ghi đè tệp khóa nếu không. (Tuy nhiên, bạn cũng cần có một khóa xung quanh read-file-check-pid-rewrite-file). Bạn sẽ tìm thấy những gì bạn cần để nhận và kiểm tra pid trong os -package. Cách phổ biến để kiểm tra xem có tồn tại một quá trình với một pid nhất định hay không là gửi cho nó một tín hiệu không gây tử vong.

Các lựa chọn thay thế khác có thể là kết hợp điều này với các bán kết đàn hoặc posix.

Mở một ổ cắm mạng, như saua đã đề xuất, có lẽ sẽ là cách dễ nhất và dễ di chuyển nhất.


3

Đối với bất kỳ ai sử dụng wxPython cho ứng dụng của họ, bạn có thể sử dụng chức năng được wx.SingleInstanceChecker ghi ở đây .

Cá nhân tôi sử dụng một lớp con wx.Appsử dụng wx.SingleInstanceCheckervà trả về Falsetừ OnInit()đó nếu có một phiên bản hiện tại của ứng dụng đã thực thi như vậy:

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

Đây là một thay thế đơn giản để wx.Appcấm nhiều trường hợp. Để sử dụng nó, chỉ cần thay thế wx.Appbằng SingleApptrong mã của bạn như sau:

app = SingleApp(redirect=False)
frame = wx.Frame(None, wx.ID_ANY, "Hello World")
frame.Show(True)
app.MainLoop()

Sau khi mã hóa một chuỗi danh sách socket cho một singleton, tôi thấy điều này, nó hoạt động tốt và tôi đã cài đặt trong một vài chương trình, tuy nhiên, tôi muốn có thêm "đánh thức" mà tôi có thể cung cấp cho singleton để tôi có thể đưa nó vào phía trước và trung tâm của một đống lớn các cửa sổ chồng lên nhau. Ngoài ra: các "tài liệu ở đây" điểm liên kết đến tài liệu tự động tạo ra khá vô dụng này là một liên kết tốt hơn
RufusVS

@RufusVS Bạn nói đúng - đó là một liên kết tài liệu tốt hơn nhiều, đã cập nhật câu trả lời.
Matt Coubrough

3

Đây là giải pháp cuối cùng chỉ dành cho Windows của tôi. Đặt phần sau vào một mô-đun, có thể được gọi là 'onlyone.py', hoặc bất cứ thứ gì. Đưa mô-đun đó trực tiếp vào tệp tập lệnh python __ main __ của bạn.

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)

Giải trình

Mã cố gắng tạo mutex với tên bắt nguồn từ đường dẫn đầy đủ đến tập lệnh. Chúng tôi sử dụng dấu gạch chéo về phía trước để tránh sự nhầm lẫn tiềm ẩn với hệ thống tệp thực.

Ưu điểm

  • Không cần cấu hình hoặc mã định danh 'ma thuật', sử dụng nó trong nhiều tập lệnh khác nhau nếu cần.
  • Không còn tệp cũ nào, mutex sẽ chết theo bạn.
  • In một tin nhắn hữu ích khi chờ đợi

3

Giải pháp tốt nhất cho việc này trên windows là sử dụng mutexes theo đề xuất của @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")

Một số câu trả lời sử dụng fctnl(cũng được bao gồm trong gói @sorin tenso) không có sẵn trên windows và nếu bạn cố gắng đóng băng ứng dụng python của mình bằng cách sử dụng một gói giống như pyinstallerthực hiện nhập tĩnh, nó sẽ gây ra lỗi.

Ngoài ra, bằng cách sử dụng phương pháp tệp khóa, sẽ tạo ra sự read-onlycố với tệp cơ sở dữ liệu (gặp phải vấn đề này với sqlite3).


2

Tôi đăng bài này như một câu trả lời vì tôi là người dùng mới và Stack Overflow sẽ không cho phép tôi bỏ phiếu.

Giải pháp của Sorin Sbarnea phù hợp với tôi trên OS X, Linux và Windows, và tôi rất biết ơn vì nó.

Tuy nhiên, tempfile.gettempdir () hoạt động theo một cách trong OS X và Windows và một cách khác trong một số / nhiều / tất cả (?) * Nixes khác (bỏ qua thực tế rằng OS X cũng là Unix!). Sự khác biệt là quan trọng đối với mã này.

OS X và Windows có các thư mục tạm thời dành riêng cho người dùng, vì vậy tệp tạm thời do một người dùng tạo sẽ không hiển thị với người dùng khác. Ngược lại, trong nhiều phiên bản * nix (tôi đã thử nghiệm Ubuntu 9, RHEL 5, OpenSolaris 2008 và FreeBSD 8), dir tạm thời là / tmp cho tất cả người dùng.

Điều đó có nghĩa là khi tệp khóa được tạo trên máy nhiều người dùng, tệp này được tạo trong / tmp và chỉ người dùng tạo tệp khóa lần đầu tiên mới có thể chạy ứng dụng.

Một giải pháp khả thi là nhúng tên người dùng hiện tại vào tên của tệp khóa.

Cần lưu ý rằng giải pháp lấy cổng của OP cũng sẽ hoạt động sai trên máy nhiều người dùng.


Đối với một số độc giả (ví dụ: tôi), hành vi mong muốn là chỉ một bản sao có thể chạy trong khoảng thời gian, bất kể có bao nhiêu người dùng tham gia. Vì vậy, các thư mục tmp cho mỗi người dùng bị hỏng, trong khi chia sẻ / tmp hoặc khóa cổng thể hiện hành vi mong muốn.
Jonathan Hartley

2

Tôi sử dụng single_processtrên gentoo của mình;

pip install single_process

ví dụ :

from single_process import single_process

@single_process
def main():
    print 1

if __name__ == "__main__":
    main()   

tham khảo: https://pypi.python.org/pypi/single_process/1.0


Thất bại trong Py3. Gói này có vẻ được cấu trúc sai.
Ekevoo

Trên Windows tôi nhận được: ImportError: Không có mô-đun tên fcntl
Andrew W. Phillips

1

Tôi tiếp tục nghi ngờ rằng phải có một giải pháp POSIXy tốt bằng cách sử dụng các nhóm quy trình, mà không cần phải nhấn vào hệ thống tệp, nhưng tôi không thể hoàn thành nó. Cái gì đó như:

Khi khởi động, quy trình của bạn sẽ gửi 'kill -0' đến tất cả các quy trình trong một nhóm cụ thể. Nếu bất kỳ quá trình nào như vậy tồn tại, nó sẽ thoát. Sau đó, nó tham gia vào nhóm. Không có quy trình nào khác sử dụng nhóm đó.

Tuy nhiên, điều này có một điều kiện về cuộc đua - nhiều quy trình có thể thực hiện điều này chính xác cùng một lúc và tất cả đều kết thúc tham gia nhóm và chạy đồng thời. Khi bạn đã thêm một số loại mutex để làm cho nó kín nước, bạn không cần các nhóm quy trình nữa.

Điều này có thể chấp nhận được nếu quy trình của bạn chỉ được bắt đầu bằng cron, một lần mỗi phút hoặc mỗi giờ, nhưng điều đó khiến tôi hơi lo lắng rằng nó sẽ sai chính xác vào ngày mà bạn không muốn.

Tôi đoán rằng đây không phải là một giải pháp rất tốt, trừ khi ai đó có thể cải thiện nó?


1

Tôi đã gặp phải vấn đề chính xác này vào tuần trước và mặc dù tôi đã tìm thấy một số giải pháp tốt, tôi quyết định tạo một gói python rất đơn giản và sạch sẽ và tải nó lên PyPI. Nó khác với tenso ở chỗ nó có thể khóa bất kỳ tên tài nguyên chuỗi nào. Mặc dù bạn chắc chắn có thể khóa__file__ để đạt được hiệu quả tương tự.

Cài đặt bằng: pip install quicklock

Sử dụng nó rất đơn giản:

[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'!

Hãy xem: https://pypi.python.org/pypi/quicklock


1
Tôi vừa cài đặt nó thông qua "pip install quicklock", nhưng khi tôi cố gắng sử dụng nó qua "from quicklock import singleton", tôi nhận được một ngoại lệ: "ImportError: không thể nhập tên 'singleton'". Đây là trên máy Mac.
greyaii

Hóa ra quicklock không hoạt động với python 3. Đó là lý do nó không thành công đối với tôi.
màu xám

Vâng, xin lỗi, nó hoàn toàn không được chứng minh trong tương lai. Tôi sẽ hoan nghênh đóng góp để làm cho nó hoạt động!
Nate Ferrero

1

Dựa trên câu trả lời của Roberto Rosario, tôi đưa ra chức năng sau:

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

Chúng ta cần xác định toàn cục SOCKETcó thể có được vì nó sẽ chỉ được thu thập rác khi toàn bộ quá trình thoát. Nếu chúng ta khai báo một biến cục bộ trong hàm, nó sẽ vượt ra khỏi phạm vi sau khi hàm thoát ra, do đó socket sẽ bị xóa.

Tất cả tín dụng sẽ thuộc về Roberto Rosario, vì tôi chỉ làm rõ và chi tiết về mã của anh ấy. Và mã này sẽ chỉ hoạt động trên Linux, như văn bản được trích dẫn sau đây từ https://troydhanson.github.io/network/Unix_domain_sockets.html giải thích:

Linux có một tính năng đặc biệt: nếu tên đường dẫn cho ổ cắm miền UNIX bắt đầu bằng byte rỗng \ 0, tên của nó sẽ không được ánh xạ vào hệ thống tệp. Vì vậy, nó sẽ không va chạm với các tên khác trong hệ thống tệp. Ngoài ra, khi máy chủ đóng ổ cắm lắng nghe miền UNIX của nó trong không gian tên trừu tượng, tệp của nó sẽ bị xóa; với các ổ cắm miền UNIX thông thường, tệp vẫn tồn tại sau khi máy chủ đóng nó.


0

ví dụ linux

Phương pháp này dựa trên việc tạo một tệp tạm thời tự động bị xóa sau khi bạn đóng ứng dụng. chương trình khởi chạy chúng tôi xác minh sự tồn tại của tệp; nếu tệp tồn tại (có một thực thi đang chờ xử lý), chương trình bị đóng; nếu không, nó sẽ tạo tệp và tiếp tục thực hiện chương trình.

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
Chào mừng bạn đến với Stack Overflow! Mặc dù câu trả lời này có thể đúng, vui lòng thêm một số giải thích. Truyền đạt logic cơ bản quan trọng hơn là chỉ đưa ra mã, vì nó giúp OP và các trình đọc khác tự khắc phục vấn đề này và các vấn đề tương tự.
CodeMouse92

Đây có phải là threadsafe không? Có vẻ như kiểm tra và tạo tệp tạm thời không phải là nguyên tử ...
coppit

0

Trên hệ thống Linux, người ta cũng có thể yêu cầu pgrep -asố lượng phiên bản, tập lệnh được tìm thấy trong danh sách quy trình (tùy chọn -a hiển thị chuỗi dòng lệnh đầy đủ). Ví dụ

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

Xóa -u $UIDnếu hạn chế áp dụng cho tất cả người dùng. Tuyên bố từ chối trách nhiệm: a) giả định rằng tên (cơ sở) của tập lệnh là duy nhất, b) có thể có các điều kiện chủng tộc.


-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
Chào mừng bạn đến với Stack Overflow! Mặc dù khối mã này có thể trả lời câu hỏi, nhưng tốt nhất là bạn có thể cung cấp một chút giải thích cho lý do tại sao nó lại như vậy. Vui lòng chỉnh sửa câu trả lời của bạn để bao gồm một mô tả như vậy.
Artjom B.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.