5.1 Thread 直接创建子线程
5.1.1 非守护线程
复杂的操作之前需要一个简单的示例开始:
# !/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author:AI悦创 @DateTime :2019/10/25 9:50 @Function :功能 Development_tool :PyCharm
# code is far away from bugs with the god animal protecting
# I love animals. They taste delicious.
import threading, time
def start():
time.sleep(1)
print(threading.current_thread().name) # 当前线程名称
print(threading.current_thread().is_alive()) # 当前线程状态
print(threading.current_thread().ident) # 当前线程的编号
print('start')
# 要使用多线程哪个函数>>>target=函数,name=给这个多线程取个名字
# 如果你不起一个名字的话,那那它会自己去起一个名字的(pid)也就是个 ident
# 类似声明
thread = threading.Thread(target=start,name='my first thread')
# 每个线程写完你不start()的话,就类似只是声明
thread.start()
print('stop')
# 输出
start
stop
my first thread
True
2968
如果有参数的话,我们就对多线程参数进行传参数。代码示例:
import threading, time
def start(num):
time.sleep(num)
print(threading.current_thread().name)
print(threading.current_thread().isAlive())
print(threading.current_thread().ident)
print('start')
thread = threading.Thread(target=start,name='my first thread', args=(1,))
thread.start()
print('stop')
解析:
我认认真看一下我们的运行结果,
start |
---|
「stop」 |
「my first thread」 |
「True」 |
「2968」 |
我们会发现并不是按我们正常的逻辑执行这一系列的代码。
而是,先执行完 「start 然后就直接 stop」 然后才会执行我们函数的其他三项。
一个线程它就直接贯穿到底了。也就是先把我们主线程里面的代码运行完,然后才会运行它里面的代码。
我们的代码并不是当代码执行到 「thread.start()」 等它执行完再执行 「print('stop')」 。而是,我们线程执行到「thread.start()」 继续向下执行,同时再执行里面的代码(也就是**start()**函数里面的代码)。(不会卡在 thread.start() 那里) 「也不会随着主线程结束而结束」
❝因为,程序在执行到 「print('stop')」 之后就是主线程结束,而里面开的线程是我们自己开的。当我们主线程执行这个 stop 就已经结束了。
这种不会随着主线程结束而销毁的,这种线程它叫做:非守护线程
❞
- 主线程会跳过创建的线程继续执行;
- 直到创建线程运行完毕;
- 程序结束;
既然,有非守护线程。那就还有守护线程。不要急,我再举个非守护线程的例子。
首先,我们可以使用 Thread 类来创建一个线程,创建时需要指定 target 参数为运行的方法名称,如果被调用的方法需要传入额外的参数,则可以通过 Thread 的 args 参数来指定。示例如下:
import threading, time
def target(second):
print(f'Threading {threading.current_thread().name} is runing')
print(f'Threading {threading.current_thread().name} sleep {second}s')
time.sleep(second)
print(f'Threading {threading.current_thread().name} ended')
print(f'Threading {threading.current_thread().name} is runing')
for i in [1, 5]:
t = threading.Thread(target=target, args=[i])
# t = threading.Thread(target=target, args=(i,))
t.start()
print(f'Threading {threading.current_thread().name} is ended')
# 输出
Threading MainThread is runing
Threading Thread-1 is runing
Threading Thread-1 sleep 1s
Threading Thread-2 is runing
Threading Thread-2 sleep 5s
Threading MainThread is ended
Threading Thread-1 ended
Threading Thread-2 ended
在这里我们首先声明了一个方法,叫作 target,它接收一个参数为 second,通过方法的实现可以发现,这个方法其实就是执行了一个 time.sleep 休眠操作,second 参数就是休眠秒数,其前后都 print 了一些内容,其中线程的名字我们通过 threading.current_thread().name 来获取出来,如果是主线程的话,其值就是 MainThread,如果是子线程的话,其值就是 Thread-*。
然后我们通过 Thead 类新建了两个线程,target 参数就是刚才我们所定义的方法名,args 以列表的形式传递。两次循环中,这里 i 分别就是 1 和 5,这样两个线程就分别休眠 1 秒和 5 秒,声明完成之后,我们调用 start 方法即可开始线程的运行。
观察结果我们可以发现,这里一共产生了三个线程,分别是主线程 MainThread 和两个子线程 Thread-1、Thread-2。另外我们观察到,主线程首先运行结束,紧接着 Thread-1、Thread-2 才接连运行结束,分别间隔了 1 秒和 4 秒。这说明主线程并没有等待子线程运行完毕才结束运行,而是直接退出了,有点不符合常理。
如果我们想要主线程等待子线程运行完毕之后才退出,可以让每个子线程对象都调用下 join 方法,实现如下:
for i in [1, 5]:
t = threading.Thread(target=target, args=[i])
t.start()
t.join()
# 输出
Threading MainThread is runing
Threading Thread-1 is runing
Threading Thread-1 sleep 1s
Threading Thread-1 ended
Threading Thread-2 is runing
Threading Thread-2 sleep 5s
Threading Thread-2 ended
Threading MainThread is ended
这样,主线程必须等待子线程都运行结束,主线程才继续运行并结束。
5.2 继承 Thread 类创建子线程
另外,我们也可以通过继承 Thread 类的方式创建一个线程,该线程需要执行的方法写在类的 run 方法里面即可。上面的例子的等价改写为:
import threading, time
class MyThread(threading.Thread):
def __init__(self, second):
threading.Thread.__init__(self)
self.second = second
def run(self):
print(f'Threading {threading.current_thread().name} is runing')
print(f'Threading {threading.current_thread().name} sleep {self.second}s')
time.sleep(self.second)
print(f'Threading {threading.current_thread().name} is ended')
print(f'Threading {threading.current_thread().name} is runing')
for i in [1, 5]:
t = MyThread(i)
t.start()
t.join()
print(f'Threading {threading.current_thread().name} is ended')
# 输出
Threading MainThread is runing
Threading Thread-1 is runing
Threading Thread-1 sleep 1s
Threading Thread-1 is ended
Threading Thread-2 is runing
Threading Thread-2 sleep 5s
Threading Thread-2 is ended
Threading MainThread is ended
可以看到,两种实现方式,其运行效果是相同的。