Python多线程中的run方法

Python中的多线程通过threading模块来实现。实现Python中的多线程有两种方式,本篇文章介绍多线程中,run方法的使用

上一篇文章中介绍了多线程的基本使用方法,在执行Thread对象的start方法之后,声明指定的target函数已经就绪,准备被CPU调用执行。当CPU的时间片分到这个线程的时候,会去执行Thread对象的run方法

这里的start和run方法一定要区分开

  • start方法是声明分到一个子线程的函数已经就绪,等待被CPU执行
  • run方法是执行到这个子线程时,自动调用的方法

拿上一篇多线程基本使用文章中的例子🌰为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import threading
import time

def func(arg):
print('func start')
time.sleep(2)
print(arg)
print('func end')

# 创建一个线程
# target 指定让线程执行的函数
t = threading.Thread(target=func, args=('PolarSnow',))
t.setDaemon(True) # 默认为False
t.start()
t.join(1) # 主线程停在这里
print('main end')

------------
func start
main end

t = threading.Thread(target=func, args=('PolarSnow',))实例化了一个Thread对象,执行了Thread类的构造方法,构造方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
def __init__(self, group=None, target=None, name=None,
args=(), kwargs=None, *, daemon=None):
"""This constructor should always be called with keyword arguments. Arguments are:

*group* should be None; reserved for future extension when a ThreadGroup
class is implemented.

*target* is the callable object to be invoked by the run()
method. Defaults to None, meaning nothing is called.

*name* is the thread name. By default, a unique name is constructed of
the form "Thread-N" where N is a small decimal number.

*args* is the argument tuple for the target invocation. Defaults to ().

*kwargs* is a dictionary of keyword arguments for the target
invocation. Defaults to {}.

If a subclass overrides the constructor, it must make sure to invoke
the base class constructor (Thread.__init__()) before doing anything
else to the thread.

"""
assert group is None, "group argument must be None for now"
if kwargs is None:
kwargs = {}
self._target = target
self._name = str(name or _newname())
self._args = args
self._kwargs = kwargs
if daemon is not None:
self._daemonic = daemon
else:
self._daemonic = current_thread().daemon
self._ident = None
self._tstate_lock = None
self._started = Event()
self._is_stopped = False
self._initialized = True
# sys.stderr is not stored in the class like
# sys.exc_info since it can be changed between instances
self._stderr = _sys.stderr
# For debugging and _after_fork()
_dangling.add(self)

在Thread的构造方法中,封装了我们传进去的两个参数

而子线程被CPU调度执行的时候,自动执行了Thread对象中的run方法,run方法源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

可以清楚的看到,这里self._target(*self._args, **self._kwargs)去执行了我们传进去的函数名所指向的函数体,简单的说,就是把target参数的那个函数执行了,并且参数就是我们传递进去的那些参数

自定义run方法

1
2
3
4
5
6
7
8
9
10
11
12
13
import threading

# 自定义一个类,继承threading.Thread
class MyThread(threading.Thread):

# 因为CPU执行子线程的时候,会自动执行Thread对象的run方法
# 所以这里我们重写父类的run方法
# 还记得Python的继承关系吗?self.run还是从原点开始找
def run(self):
pass

t = MyThread() # 创建一个线程对象
t.start() # 等待被CPU运行

模拟Thread类的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import threading


class MyThread(threading.Thread):

# 重写父类的构造方法
def __init__(self, func, args):
# 将函数名和参数封装到本类中
self.func = func
self.args = args
# 为保证继承的类正常运行,这里需要执行父类的构造方法
# 但是没有给父类的构造方法传递任何参数
# 而对象寻找参数时,也是最先从本类中寻找
super(MyThread, self).__init__()

def run(self):
# 执行参数中的函数
self.func(self.args)

# 自定义一个需要子线程运行的函数
def f(arg):
print(arg)

# 使用自定义的线程对象执行
t = MyThread(func=f, args=59)
t.start()

实际使用中还是推荐使用threading原生提供的方式,这里的run方法仅供理解threading的内部执行过程