派生线程
简单的示例
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) running2: (Thread-2) running3: (Thread-3) running4: (Thread-4) running5: (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 = args11: self.kwargs = kwargs12: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 time4: def wait_for_event(e):
5: """6: waiting for the event to be set before do anything7: :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 timeout19: :param e: Event对象的形参20: :param t: 等待事件的时间(seconds)21: :return:
22: """23: while not e.is_set():24: logging.debug("wait_for_event_timeout")
25: # 表示等待事件的时间为t26: 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 ) waiting2: (noblock ) wait_for_event_timeout3: (MainThread) waiting before calling Event.set()4: (MainThread) Event is set5: (block ) event set:True6: (noblock ) event set:True7: (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 time5: 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 + 118: finally:19: # 释放lock锁20: self.lock.release()21:22: def worker(c):
23: """24: make Counter().value + 225: :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() 可以让同一个线程的不同代码“重新获得”锁