python3之threading模块(中)

派生线程

简单的示例

  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() 可以让同一个线程的不同代码“重新获得”锁
原文地址:https://www.cnblogs.com/haoqirui/p/10322441.html