1. 程式人生 > >python3之threading模塊(中)

python3之threading模塊(中)

區別 參數 wait dna 實例 線程的狀態 state 100% second

派生線程

簡單的示例

  1: import threading
  2: import logging
  3: 
  4: class Mythread(threading.Thread):
  5:     def run(self):
  6:         logging.debug("running")
  7: logging.basicConfig(
  8:     level=logging.DEBUG,
  9:     format="(%(threadName)s) %(message)s"
 10: )
 11: for i in range(5):
 12:     t = Mythread()
 13:     t.start()

結果

  1: (Thread-1) running
  2: (Thread-2) running
  3: (Thread-3) running
  4: (Thread-4) running
  5: (Thread-5) running

如果要子類像父類(threading.Thread)那樣傳遞參數,需要重新定義構造函數

  1: import threading
  2: import logging
  3: 
  4: class Mythread(threading.Thread):
  5: 
  6:     def __init__(self, group
=None, target=None, name=None,
  7:                  args=(), kwargs=None, *, daemon=None):
  8:         super().__init__(group=group, target=target, name=name,
  9:                          daemon=daemon)
 10:         self.args = args
 11:         self.kwargs = kwargs
 12: 
 13:     def run(self):
 14:         logging.debug("running with %s and %s
" % (self.args, self.kwargs))
 15: 
 16: logging.basicConfig(
 17:     level=logging.DEBUG,
 18:     format="(%(threadName)s) %(message)s",
 19: )
 20: for i in range(5):
 21:     t = Mythread(args=(i,), kwargs={"a": "A", "b": "B"})
 22:     t.start()

結果:

  1: (Thread-1) running with (0,) and {‘a‘: ‘A‘, ‘b‘: ‘B‘}
  2: (Thread-2) running with (1,) and {‘a‘: ‘A‘, ‘b‘: ‘B‘}
  3: (Thread-3) running with (2,) and {‘a‘: ‘A‘, ‘b‘: ‘B‘}
  4: (Thread-4) running with (3,) and {‘a‘: ‘A‘, ‘b‘: ‘B‘}
  5: (Thread-5) running with (4,) and {‘a‘: ‘A‘, ‘b‘: ‘B‘}

定時器線程

Timer在一個延遲後開始工作,而且可以被任意時刻被取消。

  1: t = threading.Timer(float, target)
  2: # 設定線程名
  3: t.setName(“t1”)
  4: # 取消運行
  5: t.cancel()

線程之間同步操作

如果程序中的其他線程需要通過判斷某個線程的狀態來確定自己下一步的操作,時,事件(Event)對象是實現線程間安全通信的一種簡單的方法。
event.wait():如果event.is_set()==False將阻塞線程;
event.set(): 設置event的狀態值為True,所有阻塞池的線程激活進入就緒狀態, 等待操作系統調度;
event.clear():恢復event的狀態值為False。
event.is_set():當內部標誌為True返回True。與isSet()方法一樣。

下面是一個簡單的兩個線程的例子:

  1: import logging
  2: import threading
  3: import time
  4: def wait_for_event(e):
  5:     """
  6:     waiting for the event to be set before do anything
  7:     :param e: Event對象的形參
  8:     :return:
  9:     """
 10:     logging.debug("waiting")
 11:     # 這個線程被阻塞了
 12:     event_is_set = e.wait()
 13:     # 如果事件沒有set就永遠不會執行
 14:     logging.debug("event set:%s", event_is_set)
 15: 
 16: def wait_for_event_timeout(e, t):
 17:     """
 18:     wait t seconds and then timeout
 19:     :param e: Event對象的形參
 20:     :param t: 等待事件的時間(seconds)
 21:     :return:
 22:     """
 23:     while not e.is_set():
 24:         logging.debug("wait_for_event_timeout")
 25:         # 表示等待事件的時間為t
 26:         event_is_set = e.wait(t)
 27:         # 當事件set後或者時間超過t才會繼續執行
 28:         logging.debug("event set:%s", event_is_set)
 29:         if event_is_set:
 30:             logging.debug("processing event")
 31:         else:
 32:             logging.debug("doing other work")
 33: logging.basicConfig(
 34:     level=logging.DEBUG,
 35:     format="(%(threadName)-10s) %(message)s",
 36: )
 37: # 實例化的Event對象
 38: e = threading.Event()
 39: t1 = threading.Thread(
 40:     name="block",
 41:     target=wait_for_event,
 42:     args=(e,),
 43: )
 44: t1.start()
 45: t2 = threading.Thread(
 46:     name="noblock",
 47:     target=wait_for_event_timeout,
 48:     args=(e, 2)
 49: )
 50: t2.start()
 51: 
 52: logging.debug("waiting before calling Event.set()")
 53: time.sleep(0.3)
 54: # 啟動事件
 55: e.set()
 56: logging.debug("Event is set")

結果:

  1: (block     ) waiting
  2: (noblock   ) wait_for_event_timeout
  3: (MainThread) waiting before calling Event.set()
  4: (MainThread) Event is set
  5: (block     ) event set:True
  6: (noblock   ) event set:True
  7: (noblock   ) processing event

控制資源訪問

threading.LOCK() LocK鎖對象
threading.LOCK().acquire() 獲取底層鎖
threading.LOCK().release() 釋放底層鎖
註意python中GIL與LOCK的區別,這裏就不廢話了。
下面是一個兩個線程使用lock鎖的例子:

  1: import logging
  2: import random
  3: import threading
  4: import time
  5: class Counter:
  6:     def __init__(self, start=0):
  7:     	# 這是Counter的專用鎖對象
  8:         self.lock = threading.Lock()
  9:         self.value = start
 10:     def increment(self):
 11:         logging.debug("waiting for lock")
 12:         # 拿到lock鎖
 13:         self.lock.acquire()
 14:         try:
 15:             # 無論誰拿到這個鎖,每顯示一次value就會加一
 16:             logging.debug("acquired lock")
 17:             self.value = self.value + 1
 18:         finally:
 19:             # 釋放lock鎖
 20:             self.lock.release()
 21: 
 22: def worker(c):
 23:     """
 24:     make Counter().value + 2
 25:     :param c: Counter實例化對象
 26:     :return:
 27:     """
 28:     for i in range(2):
 29:         pause = random.random()
 30:         logging.debug("sleeping % 0.02f", pause)
 31:         time.sleep(pause)
 32:         c.increment()
 33:     logging.debug("done")
 34: logging.basicConfig(
 35:     level=logging.DEBUG,
 36:     format="(%(threadName)-10s %(message)s)"
 37: )
 38: counter = Counter()
 39: for i in range(2):
 40:     # 生成兩個線程
 41:     t = threading.Thread(target=worker, args=(counter, ))
 42:     t.start()
 43: logging.debug("waiting for worker threads")
 44: # 拿到當前python程序的主線程
 45: main_thread = threading.main_thread()
 46: 讓所有未執行完的其他線程等待。
 47: for t in threading.enumerate():
 48:     if t is not main_thread:
 49:         t.join()
 50: # 最後計算counter的value值
 51: logging.debug(("Counter:%d" % counter.value))

結果

  1: (Thread-1   sleeping  0.43)
  2: (Thread-2   sleeping  0.84)
  3: (MainThread waiting for worker threads)
  4: (Thread-1   waiting for lock)
  5: (Thread-1   acquired lock)
  6: (Thread-1   sleeping  0.96)
  7: (Thread-2   waiting for lock)
  8: (Thread-2   acquired lock)
  9: (Thread-2   sleeping  0.24)
 10: (Thread-2   waiting for lock)
 11: (Thread-2   acquired lock)
 12: (Thread-2   done)
 13: (Thread-1   waiting for lock)
 14: (Thread-1   acquired lock)
 15: (Thread-1   done)
 16: (MainThread Counter:4)

在寫代碼中,應該避免死鎖或者競爭條件。所有種類的鎖還可以如下地寫,with語句自動控制鎖的獲取與釋放。

  1: with lock_A:
  2:     # 業務代碼
  3:     statement

再入鎖

正常的Lock對象是不能請求多次的。同一個調用鏈中不同函數訪問同一個鎖的話,會產生副作用。

  1: threading.RLock() 可以讓同一個線程的不同代碼“重新獲得”鎖

python3之threading模塊(中)