Python 线程同步
1、概念
线程同步,线程间协同,通过某种计数,让一个线程访问某些数据时,其他线程不能访问这些数据,直到该线程完成对数据的操作。
2、Event
Event事件,是线程间通信机制中最简单的实现,使用一个内部的标记flag,通过flag的True或者False的变化来进行操作。
如果使用同一个Event
对象的标记Flag
。谁wait
就是等到flag
变为True
,或等到超时返回False
。不限制等待的个数。
名称 | 含义 |
---|---|
set() | 标记设置为True |
clear() | 标记设置为Flase |
is_set() | 标记 是否 为True |
wait(timeout=None) | 设置等待标记为True的时长,None为无限等待。等到返回True,未等到超时了返回False |
2.1 示例 1
import threading
import time
import logging
FORMAT = "%(asctime)s %(threadName)10s %(thread)8d %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT)
def worker(e:threading.Event):
logging.info('Before time.sleep(5). I am in thread-{}.'.format(threading.current_thread()))
time.sleep(5)
logging.info('After time.sleep(5). I am in thread-{}.'.format(threading.current_thread()))
e.set()
event = threading.Event()
logging.info(type(event))
logging.info("{} {}".format(event, event.is_set()))
event.set()
logging.info("{} {}".format(event, event.is_set()))
event.clear()
logging.info("{} {}".format(event, event.is_set()))
logging.info('Start the worker_thread.')
threading.Thread(target=worker, args=(event, ), name='worker').start()
logging.info('Before the event.wait(timeout=2)')
event.wait(timeout=2) # 阻塞等待,等待时间为2s
logging.info('After the event.wait(timeout=2)')
logging.info("{} {}".format(event, event.is_set()))
logging.info('Before the event.wait()')
event.wait() # 阻塞等待
logging.info('After the event.wait()')
logging.info("{} {}".format(event, event.is_set()))
2022-04-20 14:35:42,749 MainThread 41596 <class 'threading.Event'>
2022-04-20 14:35:42,750 MainThread 41596 <threading.Event object at 0x000001DF7A6CEE50> False
2022-04-20 14:35:42,750 MainThread 41596 <threading.Event object at 0x000001DF7A6CEE50> True
2022-04-20 14:35:42,750 MainThread 41596 <threading.Event object at 0x000001DF7A6CEE50> False
2022-04-20 14:35:42,750 MainThread 41596 Start the worker_thread.
2022-04-20 14:35:42,750 worker 47132 Before time.sleep(5). I am in thread-<Thread(worker, started 47132)>.
2022-04-20 14:35:42,750 MainThread 41596 Before the event.wait(timeout=2)
2022-04-20 14:35:44,760 MainThread 41596 After the event.wait(timeout=2)
2022-04-20 14:35:44,760 MainThread 41596 <threading.Event object at 0x000001DF7A6CEE50> False
2022-04-20 14:35:44,760 MainThread 41596 Before the event.wait()
2022-04-20 14:35:47,753 worker 47132 After time.sleep(5). I am in thread-<Thread(worker, started 47132)>.
2022-04-20 14:35:47,753 MainThread 41596 After the event.wait()
2022-04-20 14:35:47,753 MainThread 41596 <threading.Event object at 0x000001DF7A6CEE50> True
Process finished with exit code 0
2.2 示例 2
# 老板雇佣了一个工人,让他生产被杯子,老板一直等着这个工人,直到生产了5个杯子
import threading
import time
import logging
FORMAT = "%(asctime)s %(threadName)10s %(thread)8d %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT)
event = threading.Event()
def worker(e:threading.Event, n:int):
i = 0
while True:
time.sleep(1)
i += 1
logging.info('I am working. I\'ve already made {} cups.'.format(i) )
if i >= n:
event.set()
logging.info('Job done.')
break
def boss(e:threading.Event):
while not event.is_set():
logging.info('I am waiting.')
time.sleep(1)
logging.info('Good job.')
threading.Thread(target=boss, args=(event, ), name='boss').start()
threading.Thread(target=worker, args=(event, 5), name='worker').start()
2022-04-20 15:17:43,252 boss 48612 I am waiting.
2022-04-20 15:17:44,260 worker 13684 I am working. I've already made 1 cups.
2022-04-20 15:17:44,260 boss 48612 I am waiting.
2022-04-20 15:17:45,273 worker 13684 I am working. I've already made 2 cups.
2022-04-20 15:17:45,273 boss 48612 I am waiting.
2022-04-20 15:17:46,275 boss 48612 I am waiting.
2022-04-20 15:17:46,276 worker 13684 I am working. I've already made 3 cups.
2022-04-20 15:17:47,280 worker 13684 I am working. I've already made 4 cups.
2022-04-20 15:17:47,280 boss 48612 I am waiting.
2022-04-20 15:17:48,288 worker 13684 I am working. I've already made 5 cups.
2022-04-20 15:17:48,288 worker 13684 Job done.
2022-04-20 15:17:48,288 boss 48612 Good job.
Process finished with exit code 0
2.3 示例 3
# 老板雇佣了一个工人,让他生产被杯子,老板一直等着这个工人,直到生产了5个杯子
import threading
import time
import logging
FORMAT = "%(asctime)s %(threadName)10s %(thread)8d %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT)
event = threading.Event()
def worker(e:threading.Event, n:int):
i = 0
while True:
time.sleep(1)
i += 1
logging.info('I am working. I\'ve already made {} cups.'.format(i) )
if i >= n:
event.set()
logging.info('Job done.')
break
def boss(e:threading.Event):
logging.info('I am waiting.')
e.wait() # 阻塞等待
logging.info('Good job.')
threading.Thread(target=boss, args=(event, ), name='boss').start()
threading.Thread(target=worker, args=(event, 5), name='worker').start()
2022-04-20 15:19:27,761 boss 36424 I am waiting.
2022-04-20 15:19:28,771 worker 35036 I am working. I've already made 1 cups.
2022-04-20 15:19:29,782 worker 35036 I am working. I've already made 2 cups.
2022-04-20 15:19:30,791 worker 35036 I am working. I've already made 3 cups.
2022-04-20 15:19:31,799 worker 35036 I am working. I've already made 4 cups.
2022-04-20 15:19:32,802 worker 35036 I am working. I've already made 5 cups.
2022-04-20 15:19:32,802 worker 35036 Job done.
2022-04-20 15:19:32,802 boss 36424 Good job.
Process finished with exit code 0
2.4 示例 4
import threading
import logging
FORMAT = "%(asctime)s %(threadName)10s %(thread)8d %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT)
def worker(e:threading.Event, interval:int):
while not e.wait(interval):
logging.info('do sth.')
event = threading.Event()
threading.Thread(target=worker, args=(event, 1), name='worker').start()
event.wait(5)
event.set()
logging.info('Main Thread End.')
2022-04-20 15:34:55,823 worker 13952 do sth.
2022-04-20 15:34:56,830 worker 13952 do sth.
2022-04-20 15:34:57,838 worker 13952 do sth.
2022-04-20 15:34:58,842 worker 13952 do sth.
2022-04-20 15:34:59,820 MainThread 41196 Main Thread End.
Process finished with exit code 0
3、threading.Timer
threading.Timer
继承自Thread
,这个类用来定义延迟多久后执行一个函数。
threading.Timer(self, interval, function, args=None, kwargs=None)
,start
方法执行之后,Timer
对象会处于等待状态,等待了interval
秒之后,开始执行function
函数。但是,只要start以后,工作线程就启动了,只不过是延迟了interval后,才执行函数的。
Timer
是线程Thread
的子类,就是线程类,具有线程的能力和特征- 它的实例是能够延时执行目标函数的进程,在真正执行目标函数之前,都可以
cancel
它。 cancel
方法本质使用Event
类实现。这并不是说,线程提供了取消的方法。
3.1 示例 1
import threading
import logging
import time
FORMAT = "%(asctime)s %(threadName)10s %(thread)8d %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT)
def worker():
logging.info('in worker - beginning')
time.sleep(2)
logging.info('in worker - ending')
t = threading.Timer(4, worker)
t.setName('timer')
t.start()
while True:
time.sleep(1)
logging.info(threading.enumerate())
if threading.active_count() == 1:
break
2022-04-20 15:56:19,392 MainThread 42516 [<_MainThread(MainThread, started 42516)>, <Timer(timer, started 39616)>]
2022-04-20 15:56:20,403 MainThread 42516 [<_MainThread(MainThread, started 42516)>, <Timer(timer, started 39616)>]
2022-04-20 15:56:21,415 MainThread 42516 [<_MainThread(MainThread, started 42516)>, <Timer(timer, started 39616)>]
2022-04-20 15:56:22,389 timer 39616 in worker - beginning
2022-04-20 15:56:22,421 MainThread 42516 [<_MainThread(MainThread, started 42516)>, <Timer(timer, started 39616)>]
2022-04-20 15:56:23,429 MainThread 42516 [<_MainThread(MainThread, started 42516)>, <Timer(timer, started 39616)>]
2022-04-20 15:56:24,391 t