多线程threading模块中run()方法的使用

相信读过《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核心编程》一书中的两点注意事项:

  • 子类的构造函数必须先调用其基类的构造函数;
  • 之前的特殊方法__call__()在这个子类中必须要写为run()。

你可能感兴趣的:(Python)