首页 > Python基础教程 >
-
Python3标准库:threading进程中管理并发操作
1. threading进程中管理并发操作
threading模块提供了管理多个线程执行的API,允许程序在同一个进程空间并发的运行多个操作。
1.1 Thread对象
要使用Thread,最简单的方法就是用一个目标函数实例化一个Thread对象,并调用start()让它开始工作。
- import threading
- def worker():
- """thread worker function"""
- print('Worker')
- threads = []
- for i in range(5):
- t = threading.Thread(target=worker)
- threads.append(t)
- t.start()
输出有5行,每一行都是"Worker"。
如果能够创建一个线程,并向它传递参数告诉它要完成什么工作,那么这会很有用。任何类型的对象都可以作为参数传递到线程。下面的例子传递了一个数,线程将打印出这个数。
- import threading
- def worker(num):
- """thread worker function"""
- print('Worker: %s' % num)
- threads = []
- for i in range(5):
- t = threading.Thread(target=worker, args=(i,))
- threads.append(t)
- t.start()
现在这个整数参数会包含在各线程打印的消息中。
1.2 确定当前线程
使用参数来标识或命名线程很麻烦,也没有必要。每个Thread实例都有一个带有默认值的名,该默认值可以在创建线程时改变。如果服务器进程中有多个服务线程处理不同的操作,那么在这样的服务器进程中,对线程命名就很有用。
- import threading
- import time
- def worker():
- print(threading.current_thread().getName(), 'Starting')
- time.sleep(0.2)
- print(threading.current_thread().getName(), 'Exiting')
- def my_service():
- print(threading.current_thread().getName(), 'Starting')
- time.sleep(0.3)
- print(threading.current_thread().getName(), 'Exiting')
- t = threading.Thread(name='my_service', target=my_service)
- w = threading.Thread(name='worker', target=worker)
- w2 = threading.Thread(target=worker) # use default name
- w.start()
- w2.start()
- t.start()
调试输出的每一行中包含有当前线程的名。线程名列中有"Thread-1"的行对应未命名的线程w2。
大多数程序并不使用print来进行调试。logging模块支持将线程名嵌入到各个日志消息中(使用格式化代码%(threadName)s)。通过把线程名包含在日志消息中,就能跟踪这些消息的来源。
- import logging
- import threading
- import time
- def worker():
- logging.debug('Starting')
- time.sleep(0.2)
- logging.debug('Exiting')
- def my_service():
- logging.debug('Starting')
- time.sleep(0.3)
- logging.debug('Exiting')
- logging.basicConfig(
- level=logging.DEBUG,
- format='[%(levelname)s] (%(threadName)-10s) %(message)s',
- )
- t = threading.Thread(name='my_service', target=my_service)
- w = threading.Thread(name='worker', target=worker)
- w2 = threading.Thread(target=worker) # use default name
- w.start()
- w2.start()
- t.start()
而且logging是线程安全的,所以来自不同线程的消息在输出中会有所区分。
1.3 守护与非守护线程
到目前为止,示例程序都在隐式地等待所有线程完成工作之后才退出。不过,程序有 时会创建一个线程作为守护线程(daemon),这个线程可以一直运行而不阻塞主程序退出。 如果一个服务不能很容易地中断线程,或者即使让线程工作到一半时中止也不会造成数据 损失或破坏(例如,为一个服务监控工具生成“心跳”的线程),那么对于这些服务,使用 守护线程就很有用。要标志一个线程为守护线程,构造线程时便要传入daemon=True或 者要调用它的setDaemon()方法并提供参数True。默认情况下线程不作为守护线程。
- import threading
- import time
- import logging
- def daemon():
- logging.debug('Starting')
- time.sleep(0.2)
- logging.debug(