相信读过《Python核心编程》的朋友们肯定注意到,这本书在讲解threading模块的Thread类时提出了这样一个创建线程的方法,即派生Thread的子类,并创建子类的实例。
书中的示例代码如下:
import threading
from time import sleep, ctime
loops = (4, 2)
class MyThread(threading.Thread):
def __init__(self, func, args, name=''):
threading.Thread.__init__(self)
self.name = name
self.func = func
self.args = args
def run(self):
self.func(*self.args)
def loop(nloop, nsec):
print('start loop', 'at:', ctime())
sleep(nsec)
print('loop', nloop, 'done at:', ctime())
def main():
print('starting at:', ctime())
threads = []
nloops = range(len(loops))
for i in nloops:
t = MyThread(loop, (i, loops[i]), loop.__name__)
threads.append(t)
for i in nloops:
threads[i].start()
for i in nloops:
threads[i].join()
print('all DONE at:', ctime())
if __name__ == '__main__':
main()
当时,让我很困惑的一个问题是方法run()是怎样被调用的?
在我的认知里,调用类方法的方式应该是“类实例.方法名”(专用方法除外),而这段代码中并没有出现过MyThread.run()
,那么方法run()应该并不会执行才对,而run()不能执行则函数loop()也不会执行。但是运行代码后,结果却显示一切正常。
于是,我先使用调试的方法将run()方法的调用者定位到threads[i].start()
一句。
接着,我打开了threading.py库文件,找到Tread类,其中的start()方法里这样写道:
def start(self):
"""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.
"""
if not self._initialized:
raise RuntimeError("thread.__init__() not called")
if self._started.is_set():
raise RuntimeError("threads can only be started once")
with _active_limbo_lock:
_limbo[self] = self
try:
_start_new_thread(self._bootstrap, ())
except Exception:
with _active_limbo_lock:
del _limbo[self]
raise
self._started.wait()
我们只需要关注注释即可,“It arranges for the object’s run() method to be invoked in a separate thread of control.”一句表明start()方法将会调用run()方法从而启动每一个线程。
并且,在start()方法下面紧接着就是run()方法的定义:
def run(self):
"""Method representing the thread's activity.
You may override this method in a subclass. The standard run() method
invokes the callable object passed to the object's constructor as the
target argument, if any, with sequential and keyword arguments taken
from the args and kwargs arguments, respectively.
"""
try:
if self._target:
self._target(*self._args, **self._kwargs)
finally:
# Avoid a refcycle if the thread is running a function with
# an argument that has a member that points to the thread.
del self._target, self._args, self._kwargs
可以看出,官方也建议我们创建threading.Tread的一个子类来进行线程创建和调用,并且注释中说道你可以在自己创建的子类中对run()方法进行重写。
所以,当程序运行到threads[i].start()
一句的时候,start()方法会对你重写过的run()方法进行调用(具体调用的机理可以去库文件中查看),从而执行一个loop()函数线程。
最后,再引用《Python核心编程》一书中的两点注意事项: