多任务 第一讲
一、多任务
1.多任务介绍
同时做多件事情(多个任务)就叫多任务。
2.多任务理解
CPU有单核、双核和四核的,即便是单核也可以同时进行多任务处理,这是因为CPU通过时间片轮转调度的模式来进行作业,同一个程序在CPU当中快速处理完毕会退出来,为下一个程序腾出空间进行处理。
- 并发:CPU数量小于当前的执行的任务。是假的多任务
- 并行:CPU数量大于当前执行的任务。是真的多任务
3.实现多任务的三种方式
- 线程
- 进程
- 协程
二、线程
1.线程介绍
线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
2.使用线程完成多任务
举个例子:
import time
import threading # 内置模块
def demo(): # 这里叫子线程
print('1111')
time.sleep(1)
if __name__ == '__main__':
# for循环目的是为了创建多个线程对象
for i in range(3):
# 线程实例化,target参数为调用的函数名,如果函数需要置入参数则要添加args=(),详细内容看说明文档
t = threading.Thread(target=demo) # 这里叫主线程
# 启动线程
t.start()
上面的函数效果会让多个线程同时运行,即实现多任务的功能,函数中所设置的tim.sleep(1)
是为了做前后对比,在使用多线程后,3次循环内容会全部输出再同时沉睡1秒(自己试试就知道了)。
理解上我们需要注意的是,for循环的目的是为了创建多个线程对象,每次循环并不会紧接着启动线程(即执行t.start),会等待线程对象全部创建完毕再启动所有线程,从而实现多线程同时运行的功能。
写一段伪代码看一看,如何实现多个任务:
import time
import threading
def exercise():
for i in range(3):
print(f'我正在锻炼第{i}次')
time.sleep(1)
def listen_to_music():
for i in range(3):
print(f'我正在听第{i}首歌')
time.sleep(1)
if __name__ == '__main__':
t1 = threading.Thread(target=exercise)
t2 = threading.Thread(target=listen_to_music)
t1.start()
t2.start()
----------------------------------
输出结果:
我正在锻炼第0次 # 两个线程同时执行
我正在听第0首歌 #
我正在听第1首歌
我正在锻炼第1次
我正在锻炼第2次
我正在听第2首歌
注意:
主线程退出后子线程的状态依赖于它所在的进程,如果进程没有退出的话子线程依然正常运转。如果进程退出了,那么它所有的线程都会退出,所以子线程也就退出了。
主线程退出,进程等待所有子线程执行完毕后才结束。
我们可以将一个py文件看做一个进程(虽然还没学习到进程,可以简单理解为一个进程包含多个线程),上面的注意事项我们通过一段代码来进行讲解:
import threading
import time
def demo():
for i in range(3):
print('hello world')
time.sleep(1)
if __name__ == '__main__':
t = threading.Thread(target=demo) # 主线程
t.start()
print('---1---')
-------------------------------
输出结果:
hello world
---1---
hello world
hello world
上述结果可以看出,主线程执行完完毕后打印输出---1---
,子线程仍然在运行。
3.守护线程
当设置一个线程为守护线程时,此线程所属进程不会等待此线程运行结束,进程将立即结束。
同样使用上一节代码:
import threading
import time
def demo():
for i in range(3):
print('hello world')
time.sleep(1)
if __name__ == '__main__':
t = threading.Thread(target=demo)
t.setDaemon(True) # 将主线程设置为守护线程
t.start()
print('---1---')
-----------------------------
输出结果:
hello world
---1---
可以看出,子线程并未持续执行。
4. t.join()的用法
t.join()
所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一直等待其他的子线程执行结束之后,主线程再终止。
import threading
import time
def demo():
for i in range(3):
print('hello world')
time.sleep(1)
if __name__ == '__main__':
t = threading.Thread(target=demo)
t.start()
t.join() # 注意,一定要在start之后,否则会报错
print('---1---')
-------------------------------
输出结果:
hello world
hello world
hello world
---1---
从上述结果可以看出,在子线程执行完毕后才打印输出---1---
,说明子线程执行完毕后主线程才退出。
join方法有一个参数timeout,它的作用:
- 当设置守护线程时,含义是主线程对于子线程等待timeout的时间将会杀死该子线程,最后退出程序。所以说,如果有10个子线程,全部的等待时间就是每个timeout的累加和。简单的来说,就是给每个子线程一个timeout的时间,让他去执行,时间一到,不管任务有没有完成,直接杀死。
- 当设置守护线程时,含义是主线程对于子线程等待timeout的时间将会杀死该子线程,最后退出程序。所以说,如果有10个子线程,全部的等待时间就是每个timeout的累加和。简单的来说,就是给每个子线程一个timeout的时间,让他去执行,时间一到,不管任务有没有完成,直接杀死。
5.查看线程数量
使用方法threading.enumerate()
来查看当前线程的数量。
import threading
import time
def demo1():
for i in range(3):
print(f'demo1第{i}次打印')
time.sleep(1)
def demo2():
for i in range(3):
print(f'demo2第{i}次打印')
time.sleep(1)
def main():
t1 = threading.Thread(target=demo1)
t2 = threading.Thread(target=demo2)
t1.start()
t2.start()
print(threading.enumerate())
if __name__ == '__main__':
main()
---------------------------
输出结果:
demo1第0次打印
demo2第0次打印
[<_MainThread(MainThread, started 2412)>, <Thread(Thread-1, started 12132)>, <Thread(Thread-2, started 17608)>]
demo2第1次打印demo1第1次打印
demo1第2次打印
demo2第2次打印
Process finished with exit code 0
打印结果显示有一个主线程,两个子线程。
6.验证子线程的执行与创建
当调用Thread的时候,不会创建线程。
当调用Thread创建出来的实例对象的start方法的时候,才会创建线程以及开始运行这个线程。
(记住就好,不再验证)
7.继承Thread类创建线程
创建方法如下:
import threading
import time
# 1.继承Thread类
class A(threading.Thread):
# 2.改写类中的run方法,但是不要修改方法名
def run(self):
for i in range(3):
print(i)
time.sleep(1)
if __name__ == '__main__':
# 3.创建实例化对象
t = A()
# 4.运行线程
t.start()
如果在A类中有定义其它方法,那么需要在run方法中才可以实现。
8.多线程共享全局变量(线程之间的通信)
通过下述代码我们可以演示一下:
import threading
import time
num = 100
def demo1():
global num # 2.声明为全局变量
num += 1 # 1.函数作用域 访问不到外边的num 所以报错 找不到
print(f"demo----{num}")
def demo2():
print(f"demo1----{num}")
def main():
t1 = threading.Thread(target=demo1)
t2 = threading.Thread(target=demo2)
t1.start()
time.sleep(1)
t2.start()
time.sleep(1)
if __name__ == '__main__':
main()
-------------------------------------
输出结果:
demo----101
demo1----101