2013-05-15 19 views
30

Đọc qua tài liệu Python tôi đã xem qua RLock.Khi nào và làm thế nào để sử dụng RLock của Python

Ai đó có thể giải thích cho tôi (ví dụ) một tình huống trong đó RLock sẽ được ưu tiên là Lock?

Với tài liệu tham khảo đặc biệt:

  • RLock 's ‘cấp đệ quy’. Điều này hữu ích như thế nào?
  • Chủ đề "quyền sở hữu" đối tượng RLock
  • Hiệu suất?
+2

Xem thêm http://stackoverflow.com/questions/8720783/recursive-locks và http://stackoverflow.com/questions/187761/recursive-lock-mutex-vs-non-recursive-lock-mutex –

Trả lời

33

Đây là một ví dụ mà tôi thấy việc sử dụng:

hữu ích khi

  1. bạn muốn có threadsave truy cập từ bên ngoài lớp học và sử dụng các phương pháp tương tự từ bên trong lớp :

    class X: 
        def __init__(self): 
         self.a = 1 
         self.b = 2 
         self.lock = threading.RLock() 
    
        def changeA(self): 
         with self.lock: 
          self.a = self.a + 1 
    
        def changeB(self): 
         with self.lock: 
          self.b = self.b + self.a 
    
        def changeAandB(self): 
         # you can use chanceA and changeB threadsave! 
         with self.lock: 
          self.changeA() # a usual lock would block in here 
          self.changeB() 
    
  2. để đệ quy rõ ràng hơn:

    lock = threading.RLock() 
    def a(...): 
        with lock: 
    
         a(...) # somewhere inside 
    

    chủ đề khác phải đợi cho đến khi cuộc gọi đầu tiên là a hoàn tất quyền sở hữu chuỗi.

Performance

Thường thì tôi bắt đầu lập trình với các Khóa và khi trường hợp 1 hoặc 2 xảy ra, tôi chuyển sang một RLock. Until Python 3.2 RLock sẽ chậm hơn một chút do mã bổ sung. Nó sử dụng Khóa:

Lock = _allocate_lock # line 98 threading.py 

def RLock(*args, **kwargs): 
    return _RLock(*args, **kwargs) 

class _RLock(_Verbose): 

    def __init__(self, verbose=None): 
     _Verbose.__init__(self, verbose) 
     self.__block = _allocate_lock() 

Chủ đề sở hữu

trong chủ đề cho bạn có thể có được một RLock thường xuyên như bạn muốn. Các chủ đề khác cần đợi cho đến khi luồng này giải phóng tài nguyên một lần nữa.

Điều này khác với Lock ngụ ý 'quyền sở hữu chức năng gọi' (tôi sẽ gọi theo cách này): Một cuộc gọi chức năng khác phải chờ cho đến khi tài nguyên được phát hành bởi chức năng chặn cuối cùng ngay cả khi nó ở trong cùng một thread = ngay cả khi nó được gọi bởi hàm khác.

Khi sử dụng Khóa thay vì RLock

Khi bạn thực hiện cuộc gọi ra bên ngoài của các nguồn tài nguyên mà bạn không thể kiểm soát.

Đoạn code dưới đây có hai biến: a, b và RLock được sử dụng để đảm bảo một == b * 2

import threading 
a = 0 
b = 0 
lock = threading.RLock() 
def changeAandB(): 
    # this function works with an RLock and Lock 
    with lock: 
     global a, b 
     a += 1 
     b += 2 
     return a, b 

def changeAandB2(callback): 
    # this function can return wrong results with RLock and can block with Lock 
    with lock: 
     global a, b 
     a += 1 
     callback() # this callback gets a wrong value when calling changeAandB2 
     b += 2 
     return a, b 

Trong changeAandB2 Lock sẽ là lựa chọn đúng đắn mặc dù nó không block.Hoặc bạn có thể tăng cường lỗi bằng cách sử dụng RLock._is_owned(). Các chức năng như changeAandB2 có thể xảy ra khi bạn đã triển khai mẫu Observer hoặc Publisher-Subscriber và thêm khóa sau đó.

+0

Câu trả lời hay! Có '_is_owned' được ghi lại ở bất kỳ đâu không? Tôi đã mong đợi một số loại thuộc tính hoặc phương thức 'owner_name' nhưng không thể tìm thấy bất cứ điều gì được ghi lại – Awalias

+0

' _is_owned() 'bắt đầu bằng dấu gạch dưới và do đó người ta không dự kiến ​​sử dụng nó. (Nó trả về id tread của 'sys._current_treads()'.) 'Require (False)' là nonblocking và đủ trong hầu hết các trường hợp. – User

+2

Đối với người đọc trong tương lai, trong [Python 3.2 trở lên, chi phí hiệu suất của 'RLock' về cơ bản là không] (https://docs.python.org/3/whatsnew/3.2.html#optimizations), vì' RLock' là thực hiện trong C giống như 'Lock'; trước đó, nó chậm hơn vì nó thực hiện rất nhiều mã Python để bọc 'Khóa', nhưng trong 3.2+, không có chi phí (ngoài 'Khóa' có thể mở khóa từ các chủ đề khác, trong đó' RLock' chỉ có thể mở khóa được bởi chủ sở hữu, đó là sự khác biệt có ý nghĩa trong các công cụ như việc thực hiện 'Điều kiện' sử dụng mở khóa chéo để thông báo cho người phục vụ). – ShadowRanger

3
  • mức đệ quy
  • sở hữu

Một khóa nguyên thủy (Khóa) là một đồng bộ hóa ban mà không thuộc sở hữu của một chủ đề cụ thể khi bị khóa.

Đối với khóa lặp lại (RLock) Trong trạng thái bị khóa, một số chủ sở hữu khóa; ở trạng thái mở khóa, không có luồng nào sở hữu nó. Khi được gọi nếu luồng này đã sở hữu khóa, hãy tăng mức đệ quy lên một và trả về ngay lập tức. nếu thread không sở hữu khóa Nó chờ cho đến khi chủ sở hữu phát hành khóa. Thả khóa, giảm mức đệ quy. Nếu sau khi giảm số không bằng 0, hãy đặt lại khóa để mở khóa.

  • Performance

Tôi không nghĩ rằng có một số khác biệt hiệu suất chứ không phải một khái niệm.

2

Đây là trường hợp sử dụng khác cho RLock. Giả sử bạn có giao diện người dùng trên web hỗ trợ truy cập đồng thời, nhưng bạn cần quản lý một số loại quyền truy cập nhất định vào tài nguyên bên ngoài. Ví dụ, bạn phải duy trì tính nhất quán giữa các đối tượng trong bộ nhớ và các đối tượng trong cơ sở dữ liệu, và bạn có một lớp người quản lý điều khiển truy cập vào cơ sở dữ liệu, với các phương thức mà bạn phải đảm bảo được gọi theo thứ tự cụ thể và không bao giờ đồng thời.

Điều bạn có thể làm là tạo một RLock và một chuỗi người giám hộ kiểm soát quyền truy cập vào RLock bằng cách liên tục mua nó và chỉ phát hành khi được báo hiệu. Sau đó, bạn đảm bảo tất cả các phương pháp bạn cần để kiểm soát truy cập được thực hiện để có được khóa trước khi chúng chạy. Một cái gì đó như thế này:

def guardian_func(): 
    while True: 
     WebFacingInterface.guardian_allow_access.clear() 
     ResourceManager.resource_lock.acquire() 
     WebFacingInterface.guardian_allow_access.wait() 
     ResourceManager.resource_lock.release() 

class WebFacingInterface(object): 
    guardian_allow_access = Event() 
    resource_guardian = Thread(None, guardian_func, 'Guardian', []) 
    resource_manager = ResourceManager() 

    @classmethod 
    def resource_modifying_method(cls): 
     cls.guardian_allow_access.set() 
     cls.resource_manager.resource_lock.acquire() 
     cls.resource_manager.update_this() 
     cls.resource_manager.update_that() 
     cls.resource_manager.resource_lock.release() 

class ResourceManager(object): 
    resource_lock = RLock() 

    def update_this(self): 
     if self.resource_lock.acquire(False): 
      try: 
       pass # do something 
       return True 

      finally: 
       self.resource_lock.release() 
     else: 
      return False 

    def update_that(self): 
     if self.resource_lock.acquire(False): 
      try: 
       pass # do something else 
       return True 
      finally: 
       self.resource_lock.release() 
     else: 
      return False 

Bằng cách này, bạn đang đảm bảo trong những điều sau đây:

  1. Khi a thread mua lại khóa tài nguyên, nó có thể gọi các phương thức bảo vệ người quản lý tài nguyên của tự do, bởi vì RLock là đệ quy
  2. Khi chuỗi nhận được khóa tài nguyên thông qua phương thức chính trong giao diện web, tất cả quyền truy cập vào các phương thức được bảo vệ trong trình quản lý sẽ bị chặn với các chủ đề khác
  3. Chỉ có thể truy cập các phương pháp được bảo vệ trong trình quản lý bằng cách kêu gọi người giám hộ đầu tiên.
Các vấn đề liên quan