详细学习一下 Python 的线程使用方法
单线程
以逛吃为栗子,开始编码……
程序的意思就是:今天媳妇要逛街,逛街需要体力啊~ 所以我们先吃饭,吃饱了然后就购物,直到钱包发出警报~ 一共花费时间8
秒钟。
单线程:做完一件事再去做另一件事。
媳妇说了,干吃干逛多没劲,你得让我发表意见啊~好,完善一下程序,编码开始……
OK,加入自定义参数arg
,满足需求。
多线程
还是那个媳妇,还是那条街;
媳妇又想逛街了,嫌吃饭太耽误时间,想多逛一会儿,好了,去商场~
逛~吃~逛~吃~开火车~
多线程:多件事情同时做。
启动线程 -> t.start()
源码文档
"""Start the thread's activity.
It must be called at most once per thread object. It arranges for the
object's run() method to be invoked in a separate thread of control.
This method will raise a RuntimeError if called more than once on the
same thread object.
"""
该方法为启动线程,每个线程最多只能调用一次,多次调用则会引发RuntimeError
错误。调用的其实是线程的run()
方法。
吃个栗子:
import threading
from time import ctime, sleep
def c_time():
return ctime().split(' ')[3]
def eat(arg):
for i in range(2):
# 获取当前线程名称:threading.current_thread().getName()
print("%s | %s | 吃 -> %s" % (threading.current_thread().getName(), c_time(), arg))
sleep(1)
def shopping(arg):
for i in range(2):
print("%s | %s | 逛 -> %s" % (threading.current_thread().getName(), c_time(), arg))
sleep(3)
threads = []
# target -> 调用的方法;args -> 对 eat 进行传参;name -> 线程名
t1 = threading.Thread(target=eat, args=('烤面筋',), name='t1')
t2 = threading.Thread(target=shopping, args=('奥特莱斯',), name='t2')
threads.append(t1)
threads.append(t2)
if __name__ == '__main__':
for t in threads:
t.start()
print("\r%s | %s | 叮~!余额不足..." % (threading.current_thread().getName(), c_time()))
执行结果 >>>
t1 | 14:34:41 | 吃 -> 烤面筋
t2 | 14:34:41 | 逛 -> 奥特莱斯
MainThread | 14:34:41 | 叮~!余额不足...
t1 | 14:34:42 | 吃 -> 烤面筋
t2 | 14:34:44 | 逛 -> 奥特莱斯
Process finished with exit code 0
主线程跟其他线程同一时间启动,主线程执行完后,子线程会继续执行。
守护线程 - t.setDaemon()
首先看下源码如何使用
文档说明该方法通过一个bool
值来指出是否为守护线程。而且必须在start()
方法前调用,主线程默认为False
。如果不设置为守护线程程序会被无限挂起。子线程启动后,父线程也继续执行下去,当父线程执行完最后一条语句print()
后,没有等待子线程,直接就退出了,同时子线程也一同结束。
吃个栗子:
...
if __name__ == '__main__':
for t in threads:
t.setDaemon(True) # 设置守护进程
t.start()
print("\r%s | %s | 叮~!余额不足..." % (threading.current_thread().getName(), c_time()))
执行结果 >>>
t1 | 14:57:16 | 吃 -> 烤面筋
t2 | 14:57:16 | 逛 -> 奥特莱斯
MainThread | 14:57:16 | 叮~!余额不足...
Process finished with exit code 0
可以看到,t1 t2
未执行第二次循环就直接结束了,且三个线程的开始时间都是相同的。
线程等待 - t.join()
源码文档阅读
"""Wait until the thread terminates.
This blocks the calling thread until the thread whose join() method is
called terminates -- either normally or through an unhandled exception
or until the optional timeout occurs.
When the timeout argument is present and not None, it should be a
floating point number specifying a timeout for the operation in seconds
(or fractions thereof). As join() always returns None, you must call
isAlive() after join() to decide whether a timeout happened -- if the
thread is still alive, the join() call timed out.
When the timeout argument is not present or None, the operation will
block until the thread terminates.
A thread can be join()ed many times.
join() raises a RuntimeError if an attempt is made to join the current
thread as that would cause a deadlock. It is also an error to join() a
thread before it has been started and attempts to do so raises the same
exception.
"""
大概意思为:用于等待线程结束。阻塞主线程,直到子线程执行完毕。
吃个栗子:
...
if __name__ == '__main__':
t = None
for t in threads:
t.setDaemon(True)
t.start()
t.join()
print("\r%s | %s | 叮~!余额不足..." % (threading.current_thread().getName(), c_time()))
执行结果 >>>
t1 | 15:15:44 | 吃 -> 烤面筋
t2 | 15:15:44 | 逛 -> 奥特莱斯
t1 | 15:15:45 | 吃 -> 烤面筋
t2 | 15:15:47 | 逛 -> 奥特莱斯
MainThread | 15:15:50 | 叮~!余额不足...
Process finished with exit code 0
此时t.join()
在for
循环外,所以会等for
循环执行完毕,才会去执行主进程。从执行结果可以看到,两个线程是同时启动的,总耗时6
秒。相比单线程节省了2
秒的时间。
如果t.join()
放在for
循环内,线程会依次执行,相当于单线程了。输出如下:
t1 | 15:26:00 | 吃 -> 烤面筋
t1 | 15:26:01 | 吃 -> 烤面筋
t2 | 15:26:02 | 逛 -> 奥特莱斯
t2 | 15:26:05 | 逛 -> 奥特莱斯
MainThread | 15:26:08 | 叮~!余额不足...
Process finished with exit code 0
t.join()
还可以设置最大等待时间,超过设置时间后,就直接执行主进程,不再等待子进程运行完毕。
吃个栗子:
...
if __name__ == '__main__':
t = None
for t in threads:
t.setDaemon(True)
t.start()
t.join(1) # 设置最长等待时间
print("\r%s | %s | 叮~!余额不足..." % (threading.current_thread().getName(), c_time()))
执行结果 >>>
t1 | 16:15:34 | 吃 -> 烤面筋
t2 | 16:15:34 | 逛 -> 奥特莱斯
t1 | 16:15:35 | 吃 -> 烤面筋
MainThread | 16:15:35 | 叮~!余额不足...
Process finished with exit code 0
只输出 1 秒内做的事情~超过 1 秒后执行主线程结束,子线程也终止。