通过使用线程,程序可以在用一个进程空间并发地运行多个操作,threading模块建立在thread的底层特性基础之上,可以更容易地完成线程处理。
1. Thread对象(threading.Thread)
要使用Thread,最简单的方法是用一个目标函数实例化一个Thread对象,并调用start()让它开始工作。
import threading
def worker():
print "Worker"
return
threads = []
for i in range(5):
t = threading.Thread(target = worker)
threads.append(t)
t.start()
当然,也可以向线程函数中添加参数,任何类型的对象都可以作为参数传递到线程中,下面的例子是传递了一个参数,线程打印出这个数:
import threading
def worker(num):
print 'Worker: %s' % num
return
threads = []
for i in range(5):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
2. 确定当前线程(threading.currentThread)
使用参数来标识或者命名线程很麻烦,也没有必要。每个Thread实例都有一个名称,它有一个默认值,可以在创建线程时改变。如果服务器进程由处理不同操作的多个服务器线程构成,在这样的服务器进程中,对线程命名就很有用。import threading
import time
def worker():
print threading.currentThread().getName(), ' Starting'
time.sleep(2)
print threading.currentThread().getName(),' Exiting'
def my_service():
print threading.currentThread().getName(),' Starting'
time.sleep(3)
print threading.currentThread().getName(),' Exiting'
t = threading.Thread(name = 'my_service', target = my_service) # or t.setName('my_service')
w = threading.Thread(name = 'worker', target = worker)
w2 = threading.Thread(target = worker)
w.start()
w2.start()
t.start()
从结果中可以看出,每一行都包含当前线程的名称,线程名称为“Thread-1"的行对应的未命名的线程为w2.:
worker Starting
Thread-1 Starting
my_service Starting
worker Exiting
Thread-1 Exiting
my_service Exiting
大多数程序不使用print来进行调试。logging模块支持将线程名嵌入到各个日志消息中(使用格式化代码%(threadName)s)。通过将线程名包含在日志消息中,就能跟踪这些消息的来源。
import threading
import logging
import time
logging.basicConfig(
level = logging.DEBUG,
format = '[%(levelname)s] (%(threadName)-10s) %(message)s',
)
def worker():
logging.debug('Starting')
time.sleep(2)
logging.debug('Exiting')
def my_service():
logging.debug('Starting')
time.sleep(3)
logging.debug('Exiting')
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()
执行结果为:
[DEBUG] (worker ) Starting
[DEBUG] (Thread-1 ) Starting
[DEBUG] (my_service) Starting
[DEBUG] (worker ) Exiting
[DEBUG] (Thread-1 ) Exiting
[DEBUG] (my_service) Exiting
3. 守护线程(setDaemon)
上面的实例中都隐含地等待所有线程完成工作后退出。程序有时会创建一个线程作为守护线程(daemon),这个线程在后台一直运行而不阻塞主程序退出。如果一个服务无法用一种容易地方法来中断线程,或者希望工程工作到一半时终止了而不损失或者破坏数据,对于这些服务,使用守护线程就很有用。要标志一个线程为守护线程,需要调用其setDaemon()方法并提供参数TRUE。默认情况下线程不作为守护线程。import threading
import time
import logging
logging.basicConfig(level = logging.DEBUG,
format = '(%(threadName)-10s) %(message)s',
)
def daemon():
logging.debug('Stating')
time.sleep(3)
logging.debug('Exiting')
d = threading.Thread(name = 'daemon', target = daemon)
d.setDaemon(True)
def non_daemon():
logging.debug('Starting')
logging.debug('Exiting')
t = threading.Thread(name = 'non-daemon', target = non_daemon)
d.start()
t.start()
#d.join(1)
#print 'd.isAlive()', d.isAlive()
#t.join()
输出结果为:
(daemon ) Stating
(non-daemon) Starting
(non-daemon) Exiting
要等待一个守护进程结束工作,可使用join()方法,默认情况下jion会无限阻塞,也可以给jion传入参数,表示等待线程结束的时间(秒),即使在等待时间内线程没有结束,jion也会返回。将上面的程序最后三行解开,从下面结果中可以看出,由于传入参数小于守护进程睡眠时间,所以join()返回之后这个线程任然存活。
(daemon ) Stating
(non-daemon) Starting