2011-03-16 71 views
1

我想調試使用各種鎖的多線程Python應用程序。記錄鎖獲取和釋放多線程應用程序中的調用

而不是地方log.debug(...)語句各地拍來跟蹤何時何地鎖獲取和釋放,我的想法是裝飾方法threading.Lock.acquire()threading.Lock.release(),並且用類似下面的前綴它們的調用:

log.debug("lock::acquire() [%s.%s.%s]" % 
      (currentThread().getName(), 
      self.__class__.__name__, 
      sys._getframe().f_code.co_name)) 

日誌是一些全球記錄對象 - 爲討論的緣故。

現在最好的名字在日誌條目中「鎖定」將會在運行時得到,因此不管是哪個的鎖定對象,這些方法是基於日誌調用將輸出它的名字,裝飾操作,當前線程,類,以及調用操作(獲取|釋放)的功能。

免責聲明:我承認上面給出的代碼對於任何這樣的裝飾器實現都是不夠的。只是提供我認爲可以實現的東西的味道。

有沒有人知道我是否可以裝飾標準庫方法,而不必修改線程庫的原始源代碼,即從我的調用應用程序代碼中?

也許我在吠叫錯誤的樹,並且有更好的方法來實現相同的目的,而不使用裝飾器?如果確實如此,非常感謝任何指導。

解決方案:(由lazyr啓發)

下面的代碼記錄鎖定操作和給我的方法/函數調用鎖定操作的名稱(我也適應代碼條件下正常工作及其附加的wait()通知()方法):

# Class to wrap Lock and simplify logging of lock usage 
class LogLock(object): 
    """ 
    Wraps a standard Lock, so that attempts to use the 
    lock according to its API are logged for debugging purposes 

    """ 
    def __init__(self, name, log): 
     self.name = str(name) 
     self.log = log 
     self.lock = threading.Lock() 
     self.log.debug("{0} created {1}".format(
      inspect.stack()[1][3], self.name)) 

    def acquire(self, blocking=True): 
     self.log.debug("{0} trying to acquire {1}".format(
      inspect.stack()[1][3], self.name)) 
     ret = self.lock.acquire(blocking) 
     if ret == True: 
      self.log.debug("{0} acquired {1}".format(
       inspect.stack()[1][3], self.name)) 
     else: 
      self.log.debug("{0} non-blocking acquire of {1} lock failed".format(
       inspect.stack()[1][3], self.name)) 
     return ret 

    def release(self): 
     self.log.debug("{0} releasing {1}".format(inspect.stack()[1][3], self.name)) 
     self.lock.release() 

    def __enter__(self): 
     self.acquire() 

    def __exit__(self, exc_type, exc_val, exc_tb): 
     self.release() 
     return False # Do not swallow exceptions 

凡日誌實例傳遞給LogLock。

# With the following format 
log_format = \ 
     logging.Formatter('%(asctime)s %(levelname)s %(threadName)s %(message)s') 
+0

如果您要監視的代碼在您的控制之下,那麼創建Lock的後代並讓您的代碼使用它會更容易。即使@decorators在'__init__'構造函數的每個實例上應用時也更容易。 – Apalala 2011-03-16 15:44:50

回答

3

最近我只是你的問題:初始化logging.Formatter如下,以給我調用線程的身份定義。我設置我的記錄器來自動記錄線程名稱,就像在this的答案。我發現這是不可能繼承鎖,所以我不得不把它包起來,像這樣:

class LogLock(object): 
    def __init__(self, name): 
     self.name = str(name) 
     self.lock = Lock() 

    def acquire(self, blocking=True): 
     log.debug("{0:x} Trying to acquire {1} lock".format(
      id(self), self.name)) 
     ret = self.lock.acquire(blocking) 
     if ret == True: 
      log.debug("{0:x} Acquired {1} lock".format(
       id(self), self.name)) 
     else: 
      log.debug("{0:x} Non-blocking aquire of {1} lock failed".format(
       id(self), self.name)) 
     return ret 

    def release(self): 
     log.debug("{0:x} Releasing {1} lock".format(id(self), self.name)) 
     self.lock.release() 

    def __enter__(self): 
     self.acquire() 

    def __exit__(self, exc_type, exc_val, exc_tb): 
     self.release() 
     return False # Do not swallow exceptions 

我記錄的對象的ID,所以我可以多個鎖之間具有相同名稱的區別,你可能不需要它。