一. 进程和线程
进程是资源分配的单位。当开启一个程序便创建了一个进程。各进程之间相互独立。每一个程序至少要有一个进程,每一个进程至少要有一个线程
线程是CPU调度的最小单位。各个线程除了拥有一些必需的资源外,会共享进程的资源。进程可以理解为一个公司,拥有很多资源,比如,办公室,桌椅等。而线程可以理解为公司内的员工,可以分配去执行工作任务。
二.python中创建多线程的方式
当没有创建多线程时,程序只能按照一定的顺序一步一步执行。按照下述例子,先执行fun函数,再接着向下执行。
def fun():
for i in range(10):
print("fun函数", i, end=" ")
if __name__ == "__main__":
fun()
for i in range(10):
print("main", i, end=" ")
执行结果为:
fun函数 0 fun函数 1 fun函数 2 fun函数 3 fun函数 4 fun函数 5 fun函数 6 fun函数 7 fun函数 8 fun函数 9 main 0 main 1 main 2 main 3 main 4 main 5 main 6 main 7 main 8 main 9
当使用多线程时,程序会并发执行
- 利用threading里的Thread模块
注意:此时程序中i的范围如果还是10的话,因为执行速度很快,所以看不出效果。这里改为1000,就能够看到他们是异步执行的。
from threading import Thread
def fun():
for i in range(1000):
print("fun函数", i)
if __name__ == "__main__":
t = Thread(target=fun) # 创建了一个线程
t.start() # 可以开始执行该线程
for i in range(1000):
print("main", i)
- 重写Thread模块中的run函数
注意:虽然重写了run函数,但是开始运行的时候还是使用start
from threading import Thread
class MyThread(Thread): # 定义一个类继承Thread
def run(self):
for i in range(1000):
print("子线程", i)
if __name__ == "__main__":
t = MyThread() # 创建实例化对象
t.start() # 可以开始执行该线程
for i in range(1000):
print("main", i)
如果想给函数传参以区分不同线程执行的同一个函数,可以使用以下形式
注意:传递参数必须是元组
from threading import Thread
def fun(name):
for i in range(1000):
print(name, i)
if __name__ == "__main__":
t1 = Thread(target=fun, args=("周杰伦",)) # 传递参数必须是元组
t1.start() # 可以开始执行该线程
t2 = Thread(target=fun, args=("王力宏",))
t2.start()
三. 多进程
多进程和多线程创建方法相似。开进程比较耗费资源,一般不使用。
from multiprocessing import Process
def fun():
for i in range(1000):
print("子进程", i)
if __name__ == "__main__":
p = Process(target=fun)
p.start()
for i in range(1000):
print("主进程",i)
四.线程池
线程池:一次性开辟一些线程,用户直接给线程池子提交任务。线程任务的调度交给线程池来完成。如下代码开辟了一个线程数为50的线程池。提交100次任务。
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def fn(name):
for i in range(1000):
print(name, i)
if __name__ == "__main__":
# 创建线程池
with ThreadPoolExecutor(50) as t:
for i in range(100):
t.submit(fn, name=f"线程{i}")
# 等待线程池中的任务全部执行完毕再执行
print("123")
如果要用进程池,将ThreadPoolExecutor换成ProcessPoolExecutor
五.协程
当遇到某些操作时,程序会处于阻塞状态。如:
sleep(3) 强制等待
input() 等待输入
request.get() 网络请求返回数据之前,阻塞
故一般情况下,当程序处于IO操作的时候,线程都会处于阻塞状态
在单线程条件下:
协程:当程序遇见IO操作,可以选择性的切换到其他任务上。
在微观上是一个任务一个任务的进行切换,切换条件是IO操作
在宏观上,我们能看到的其实是多个任务一起在执行——多任务异步操作
注意:如果使用time.sleep,则运行程序,耗时9s多,异步操作并没有起到作用。当程序出现了同步操作的时候,异步就中断了
需要将time.sleep换为await asyncio.sleep(),此时运行耗费4s多
import asyncio
import time
async def func1():
print("你好啊,我叫塞里亚")
# time.sleep(3) # 当程序出现了同步操作的时候,异步就中断了
await asyncio.sleep(3)
print("你好啊,我叫塞里亚")
async def func2():
print("你好啊,我叫周杰伦")
# time.sleep(3) # 当程序出现了同步操作的时候,异步就中断了
await asyncio.sleep(3) # 异步操作代码
print("你好啊,我叫周杰伦")
async def func3():
print("你好啊,我叫张三")
# time.sleep(3)
await asyncio.sleep(3)
print("你好啊,我叫张三")
async def main():
# 第一种写法
# f1 = func1()
# await f1 # 一般await挂起操作放在协程对象前面
# 第二种写法(推荐)
tasks = [
asyncio.create_task(func1()),
asyncio.create_task(func2()),
asyncio.create_task(func3())]
# python3.8以后需要加上asyncio.create_task
await asyncio.wait(tasks)
if __name__ == "__main__":
t1 = time.time()
asyncio.run(main())
t2 = time.time()
print(t2-t1)
使用requests.get()时,需要将同步代码变为异步操作。需要用到aiohttp
安装
pip install aiohttp
s = aiohtto.ClientSession() 等价于 requests
接着正常使用s.get() s.post()
下载图片的函数代码