threading 模块是基于 thread 底层的模块,对 thread 做了一些包装的,可以更加方便的被使用的多线程包
结构:
import threading
# 创建线程对象
对象名 = threading.Thread(target=函数名)
对象名.start()
| 参数名 | 可选 | 含义 |
|---|---|---|
| target | 必填 | 要调用的可调用对象。默认为“无”,表示不调用任何内容。 |
| group | 可选 | 应为 None;保留用于实现 ThreadGroup 类时的将来扩展 |
| name | 可选 | 是线程名。默认情况下,唯一的名称由“Thread-N”构成,其中 N 是一个小的十进制数。 |
| args | 可选 | 是目标调用的参数元组。默认为()。 |
| kwargs | 可选 | 是目标调用的关键字参数字典。默认为{}。 |
| daemon | 可选 | 布尔值,指定线程是否为守护线程。守护线程会在主线程退出时自动退出。默认值为False,即非守护线程。 |
demo:
import threading
def say_something():
print("hello world!")
if __name__ == '__main__':
for i in range(5):
t = threading.Thread(target=say_something)
t.start()运行结果:
hello world!
hello world!
hello world!
hello world!hello world!
说明:
可以明显看出使用了多线程并发的操作,花费时间要短很多
当调用
start()时,才会真正地创建线程,并且开始执行
程序开始运行解释器自动创建的线程
graph TD
A[主线程开始运行] --> B(子线程开始运行)
B --> C[主线程结束运行]
F[线程运行状态]
代码验证:
import threading
from time import sleep, ctime
def sing():
for i in range(1, 4):
print("唱歌---%s" % i)
sleep(1)
def dance():
for i in range(1, 4):
print("跳舞---%s" % i)
sleep(1)
if __name__ == '__main__':
print("——————开始工作————:%s" % ctime())
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.start()
t2.start()
sleep(5) # 这里延时是为了等待线程运行结束
print('---结束工作---:%s' % ctime())运行结果:
——————开始工作————:Wed Jun 17 14:50:24 2020
唱歌---1
跳舞---1
唱歌---2
跳舞---2
唱歌---3
跳舞---3
---结束工作---:Wed Jun 17 14:50:29 2020
验证结论:
当整个程序代码运行结束,主线程才会结束也就是程序终止
线程执行的顺序是无序的
注意:
在 Python 中,主线程不会默认等待子线程运行结束再结束。
上面的例子是主动调用了 sleep(5)的方法强制让主流程等待子流程结束所以才会出现那样的结果。
如果主线程结束了,即使子线程还在执行,程序也会随之终止。这是因为子线程默认是非守护线程(daemon=False),而非守护线程的执行不会影响主线程的结束。
如果你希望主线程等待子线程运行结束再结束,可以使用
join()方法。join()方法会阻塞主线程,直到调用它的线程(子线程)执行完毕。
方法:
threading.enumerate() 返回线程对象列表
结构:
import threading
# 创建线程对象
对象名 = threading.Thread(target=函数名)
对象名.start()
print("线程数量为:%s"%threading.enumerate())
demo:
import threading
from time import sleep
def sing():
for i in range(1, 4):
print("唱歌---%s" % i)
sleep(1)
def dance():
for i in range(1, 4):
print("跳舞---%s" % i)
sleep(1)
if __name__ == '__main__':
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.start()
t2.start()
while True:
length = len(threading.enumerate())
print(threading.enumerate())
print('当前运行的线程数为:%d'%length)
if length<=1:
break
sleep(0.5)运行结果:
唱歌---1
跳舞---1
[<_MainThread(MainThread, started 4375117248)>, <Thread(Thread-1, started 123145525432320)>, <Thread(Thread-2, started 123145542221824)>]
当前运行的线程数为:3
[<_MainThread(MainThread, started 4375117248)>, <Thread(Thread-1, started 123145525432320)>, <Thread(Thread-2, started 123145542221824)>]
当前运行的线程数为:3
跳舞---2
唱歌---2
[<_MainThread(MainThread, started 4375117248)>, <Thread(Thread-1, started 123145525432320)>, <Thread(Thread-2, started 123145542221824)>]
当前运行的线程数为:3
[<_MainThread(MainThread, started 4375117248)>, <Thread(Thread-1, started 123145525432320)>, <Thread(Thread-2, started 123145542221824)>]
当前运行的线程数为:3
跳舞---3
唱歌---3
[<_MainThread(MainThread, started 4375117248)>, <Thread(Thread-1, started 123145525432320)>, <Thread(Thread-2, started 123145542221824)>]
当前运行的线程数为:3
[<_MainThread(MainThread, started 4375117248)>, <Thread(Thread-1, started 123145525432320)>, <Thread(Thread-2, started 123145542221824)>]
当前运行的线程数为:3
[<_MainThread(MainThread, started 4375117248)>]
当前运行的线程数为:1
可以看到每次线程列表中都有三个线程数,一个主线程,2 个子线程。
为了让每个线程的封装性更完美,所以使用 threading 模块时,往往会定义一个新的子类
class,只要继承 threading.Thread 就可以了,然后重写 run 方法。
结构:
# 定义一个类继承threading.Thread
class 类名称(threading.Thread):
# 提供run()方法
def run(self):
代码
demo:
import threading
class MyThread(threading.Thread):
def run(self):
print("%s:线程运行了" % self.name)
if __name__ == '__main__':
a = MyThread()
a.start()
b = MyThread()
b.start()运行结果:
Thread-1:线程运行了Thread-2:线程运行了
说明:
python 的 threading.Thread 类有一个 run 方法,用于定义线程的功能函数,可以在
自己的线程类中覆盖该方法。而创建自己的线程实例后,通过 Thread 类的 start 方
法,可以启动该线程,交给 python 虚拟机进行调度,当该线程获得执行的机会时,
就会调用 run 方法执行线程。
demo:
import threading
from time import sleep
class MyThread(threading.Thread):
def run(self):
sleep(1)
print("%s:线程运行了" % self.name)
if __name__ == '__main__':
for i in range(10):
a = MyThread()
a.start()运行结果:
Thread-1:线程运行了
Thread-2:线程运行了
Thread-7:线程运行了
Thread-6:线程运行了
Thread-3:线程运行了
Thread-10:线程运行了
Thread-9:线程运行了
Thread-5:线程运行了
Thread-4:线程运行了
Thread-8:线程运行了
从代码和执行结果我们可以看出,多线程程序的执行顺序是不确定的。当执行到 sleep 语句时,线程将被阻塞(Blocked),到 sleep 结束后,线程进入就绪(Runnable)状态,等待调度。而线程调度将自行选择一个线程执行。
上面的代码中只能保证每个线程都运行完整个 run 函数,但是线程的启动顺序、run 函数中每次循环的执行顺序都不能确定。
每个线程默认有一个名字,尽管上面的例子中没有指定线程对象的 name,但是 python 会自动为线程指定一个名字。
当线程的 run()方法结束时该线程完成。
无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式。
如何保持线程按顺序运行?
在两个子线程之间添加上足量的延迟