为了账号安全,请及时绑定邮箱和手机立即绑定

python基础线程-管理并发线程

标签:
CSS3

线程模块建立在线程的底层特性之上,使线程的工作变得更简单、更像python。使用线程允许程序在同一进程空间中并发运行多个操作。

使用线程最简单的方法是用目标函数实例化它,然后调用start()让它开始工作。

import threadingdef worker(): “”“线程worker函数”"" print(‘Worker’) returnthreads = []for i in range(5): t = threading.Thread(target=worker) threads.append(t) t.start()

结果:输出为五行,每行上都有“Worker”

$ python3 threading_simple.pyWorkerWorkerWorkerWorkerWorker

能够生成一个线程并传递参数来告诉它要做什么工作是很有用的。这个例子传递一个数字,然后线程打印这个数字。

import threadingdef worker(num): “”“线程worker函数”"" print(‘Worker: {num}’ ) returnthreads = []for i in range(5): t = threading.Thread(target=worker, args=(i,)) threads.append(t) t.start()

现在每个线程打印的消息包含一个数字:

$ python3 -u threading_simpleargs.pyWorker: 0Worker: 1Worker: 2Worker: 3Worker: 4

使用参数来标识或命名线程很麻烦,而且没有必要。每个Thread实例都有一个具有默认值的名称,该名字可以在创建线程时更改。命名线程在具有多个服务线程来处理不同操作的服务器进程中很有用。

import threadingimport timedef worker(): print(threading.currentThread().getName(), ‘开始运行’) time.sleep(2) print(threading.currentThread().getName(), ‘结束运行’)def my_service(): print(threading.currentThread().getName(), ‘开始运行’) time.sleep(3) print(threading.currentThread().getName(), ‘结束运行’)t = threading.Thread(name=‘my_service’, target=my_service)w = threading.Thread(name=‘worker’, target=worker)w2 = threading.Thread(target=worker) # 使用默认名字w.start()w2.start()t.start()

调试输出包括每行上当前线程的名称。“线程名称”列中带有“Thread-1”的行对应于未命名的线程w2。

$ python -u threading_names.pyworker Thread-1 开始运行my_service 开始运行开始运行Thread-1worker 结束运行结束运行my_service 结束运行

大多数程序不使用打印进行调试。logging支持使用格式化程序代码%(threadName)在每个日志消息中嵌入线程名称。在日志消息中包含线程名称可以更容易地将这些消息追溯到其源。

import loggingimport threadingimport timelogging.basicConfig(level=logging.DEBUG, format=’[%(levelname)s] (%(threadName)-10s) %(message)s’, )def worker(): logging.debug(‘开始运行’) time.sleep(2) logging.debug(‘结束运行’)def my_service(): logging.debug(‘开始运行’) time.sleep(3) logging.debug(‘结束运行’)t = threading.Thread(name=‘my_service’, target=my_service)w = threading.Thread(name=‘worker’, target=worker)w2 = threading.Thread(target=worker) # 使用默认名字w.start()w2.start()t.start()

logging是线程安全的,因此来自不同线程的消息在输出中保持不同。

$ python threading_names_log.py[DEBUG] (worker ) 开始运行[DEBUG] (Thread-1 ) 开始运行[DEBUG] (my_service) 开始运行[DEBUG] (worker ) 结束运行[DEBUG] (Thread-1 ) 结束运行[DEBUG] (my_service) 结束运行

到目前为止,示例程序已隐式地等待退出,直到所有线程都完成了它们的工作。有时程序生成一个线程作为守护进程运行,而该线程在运行时不会阻止主程序退出。使用守护进程线程对于那些可能无法轻松中断线程或让线程在其工作过程中死亡不会丢失或损坏数据的服务(例如,为服务监视工具生成“心跳”的线程)非常有用。要将线程标记为守护程序,请使用布尔参数调用其setDaemon()方法。默认情况下,线程不是守护程序,因此传递True将打开守护程序模式。

import threadingimport timeimport logginglogging.basicConfig(level=logging.DEBUG, format=’(%(threadName)-10s) %(message)s’, )def daemon(): logging.debug(‘开始运行’) time.sleep(2) logging.debug(‘结束运行’)d = threading.Thread(name=‘daemon’, target=daemon)d.setDaemon(True)def non_daemon(): logging.debug(‘开始运行’) logging.debug(‘结束运行’)t = threading.Thread(name=‘non-daemon’, target=non_daemon)d.start()t.start()

请注意,输出不包括来自守护进程线程的“结束运行”消息,因为所有非守护进程线程(包括主线程)都在守护进程线程从其两秒钟的睡眠中唤醒之前退出。

$ python threading_daemon.py(daemon ) 开始运行(non-daemon) 开始运行(non-daemon) 结束运行

要等到守护进程线程完成其工作,请使用join()方法。

import threadingimport timeimport logginglogging.basicConfig(level=logging.DEBUG, format=’(%(threadName)-10s) %(message)s’, )def daemon(): logging.debug(‘开始运行’) time.sleep(2) logging.debug(‘结束运行’)d = threading.Thread(name=‘daemon’, target=daemon)d.setDaemon(True)def non_daemon(): logging.debug(‘开始运行’) logging.debug(‘结束运行’)t = threading.Thread(name=‘non-daemon’, target=non_daemon)d.start()t.start()d.join()t.join()

等待守护线程使用join()退出意味着它有机会生成“结束运行”消息。

$ python threading_daemon_join.py(daemon ) 开始运行(non-daemon) 开始运行(non-daemon) 结束运行(daemon ) 结束运行

默认情况下,join()无限期阻塞。也可以传递一个超时参数(一个浮点数,表示等待线程变为非活动状态的秒数)。如果线程没有在超时时间内完成,join()仍然返回。

import threadingimport timeimport logginglogging.basicConfig(level=logging.DEBUG, format=’(%(threadName)-10s) %(message)s’, )def daemon(): logging.debug(‘开始运行’) time.sleep(2) logging.debug(‘结束运行’)d = threading.Thread(name=‘daemon’, target=daemon)d.setDaemon(True)def non_daemon(): logging.debug(‘开始运行’) logging.debug(‘结束运行’)t = threading.Thread(name=‘non-daemon’, target=non_daemon)d.start()t.start()d.join(1)# python2写法print (‘d.isAlive()’, d.isAlive())# python3写法print (‘d.is_alive()’, d.is_alive())t.join()

由于传递的超时值小于守护进程线程的睡眠时间,因此join()返回后,线程仍然是“活动的”。

$ python threading_daemon_join_timeout.py(daemon ) 开始运行(non-daemon) 开始运行(non-daemon) 结束运行d.isAlive() Trued.is_alive() True

没有必要保留所有守护进程线程的显式句柄,以确保它们在退出主进程之前已完成。enumerate()返回活动线程实例的列表。该二手列表包含当前线程,并且由于不允许加入当前线程(这会导致死锁情况),因此必须跳过它。

import randomimport threadingimport timeimport logginglogging.basicConfig(level=logging.DEBUG, format=’(%(threadName)-10s) %(message)s’, )def worker(): “”“线程worker函数”"" t = threading.currentThread() pause = random.randint(1,5) logging.debug(‘sleeping %s’, pause) time.sleep(pause) logging.debug(‘ending’) returnfor i in range(3): t = threading.Thread(target=worker) t.setDaemon(True) t.start()main_thread = threading.currentThread()for t in threading.enumerate(): if t is main_thread: continue logging.debug(f’joining { t.getName()}’) t.join()

由于worker睡眠的时间是随机的,所以这个程序的输出可能会有所不同。应该是这样的:

$ python threading_enumerate.py(Thread-1 ) sleeping 3(Thread-2 ) sleeping 2(Thread-3 ) sleeping 5(MainThread) joining Thread-1(Thread-2 ) ending(Thread-1 ) ending(MainThread) joining Thread-3(Thread-3 ) ending(MainThread) joining Thread-

在启动时,线程进行一些基本的初始化,然后调用其run()方法,该方法调用传递给构造函数的目标函数。若要创建Thread的子类,请重写run()以执行任何必要的操作。

import threadingimport logginglogging.basicConfig(level=logging.DEBUG, format=’(%(threadName)-10s) %(message)s’, )class MyThread(threading.Thread): def run(self): logging.debug(‘running’) returnfor i in range(5): t = MyThread() t.start()

忽略run()的返回值。

$ python threading_subclass.py(Thread-1 ) running(Thread-2 ) running(Thread-3 ) running(Thread-4 ) running(Thread-5 ) running

因为传递给Thread构造函数的args和kwargs值保存在私有变量中,因此不容易从子类访问它们。要将参数传递给自定义线程类型,请重新定义构造函数以将值保存在实例属性中,该属性可以在子类中看到。

import threadingimport logginglogging.basicConfig(level=logging.DEBUG, format=’(%(threadName)-10s) %(message)s’, )class MyThreadWithArgs(threading.Thread): def init(self, group=None, target=None, name=None, args=(), kwargs=None, verbose=None): threading.Thread.init(self, group=group, target=target, name=name, verbose=verbose) self.args = args self.kwargs = kwargs return def run(self): logging.debug(‘running with %s and %s’, self.args, self.kwargs) returnfor i in range(5): t = MyThreadWithArgs(args=(i,), kwargs={‘a’:‘A’, ‘b’:‘B’}) t.start()

MyThreadWithArgs与Thread使用相同的API,但是与其他任何类一样,另一个类可以轻松地更改构造函数方法以采用与线程目的更直接相关的更多或不同的参数。

$ python threading_subclass_args.py(Thread-1 ) running with (0,) and {‘a’: ‘A’, ‘b’: ‘B’}(Thread-2 ) running with (1,) and {‘a’: ‘A’, ‘b’: ‘B’}(Thread-3 ) running with (2,) and {‘a’: ‘A’, ‘b’: ‘B’}(Thread-4 ) running with (3,) and {‘a’: ‘A’, ‘b’: ‘B’}(Thread-5 ) running with (4,) and {‘a’: ‘A’, ‘b’: ‘B’}

Timer提供了一个将Thread子类化的原因的示例,它也包含在线程中。计时器在延迟之后开始工作,并且可以在该延迟时间段内的任何时间取消。

import threadingimport timeimport logginglogging.basicConfig(level=logging.DEBUG, format=’(%(threadName)-10s) %(message)s’, )def delayed(): logging.debug(‘worker running’) returnt1 = threading.Timer(3, delayed)t1.setName(‘t1’)t2 = threading.Timer(3, delayed)t2.setName(‘t2’)logging.debug(‘starting timers’)t1.start()t2.start()logging.debug(‘waiting before canceling %s’, t2.getName())time.sleep(2)logging.debug(‘canceling %s’, t2.getName())t2.cancel()logging.debug(‘done’)

请注意,第二个计时器从不运行,而第一个计时器似乎在主程序的其余部分完成后运行。因为它不是守护进程线程,所以当主线程完成时,它是隐式连接的。

$ python threading_timer.py(MainThread) starting timers(MainThread) waiting before canceling t2(MainThread) canceling t2(MainThread) done(t1 ) worker running

虽然使用多个线程的目的是分离出单独的操作以并发运行,但有时能够在两个或多个线程中同步这些操作是很重要的。线程之间通信的一个简单方法是使用事件对象。事件管理内部标志,调用者可以set()或clear()。其他线程可以wait()设置set(),有效地阻止进程,直到允许继续。

import loggingimport threadingimport timelogging.basicConfig(level=logging.DEBUG, format=’(%(threadName)-10s) %(message)s’, ) def wait_for_event(e): “”“Wait for the event to be set before doing anything”"" logging.debug(‘wait_for_event starting’) event_is_set = e.wait() logging.debug(‘event set: %s’, event_is_set)def wait_for_event_timeout(e, t): “”“Wait t seconds and then timeout”"" while not e.isSet(): logging.debug(‘wait_for_event_timeout starting’) event_is_set = e.wait(t) logging.debug(‘event set: %s’, event_is_set) if event_is_set: logging.debug(‘processing event’) else: logging.debug(‘doing other work’)e = threading.Event()t1 = threading.Thread(name=‘block’, target=wait_for_event, args=(e,))t1.start()t2 = threading.Thread(name=‘non-block’, target=wait_for_event_timeout, args=(e, 2))t2.start()logging.debug(‘Waiting before calling Event.set()’)time.sleep(3)e.set()logging.debug(‘Event is set’)

wait()方法在等待表示时间的参数之前占用了秒数。它返回一个布尔值,指示是否设置了事件,因此调用者知道wait()返回的原因。isSet()方法可以单独用于事件,而不必担心阻塞。

在本例中,wait_for_event_timeout()检查事件状态,不会无限期阻塞。wait_for_event()会阻塞对wait()的调用,直到事件状态更改后才会返回。

$ python threading_event.py(block ) wait_for_event starting(non-block ) wait_for_event_timeout starting(MainThread) Waiting before calling Event.set()(non-block ) event set: False(non-block ) doing other work(non-block ) wait_for_event_timeout starting(MainThread) Event is set(block ) event set: True(non-block ) event set: True(non-block ) processing event

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消