【4.0】知识点小结(线程进阶)
【4.0】知识点小结(线程进阶)
【一】什么是死锁与递归锁
- 死锁是指两个或多个进程,在执行过程中,因争夺资源而造成了互相等待的一种现象。
- 即两个或多个进程持有各自的锁并试图获取对方持有的锁,从而导致被阻塞,不能向前执行,最终形成僵局。
- 在这种情况下,系统资源利用率极低,系统处于一种死循环状态。
- 递归锁(也叫可重入锁)是一种特殊的锁,它允许一个线程多次请求同一个锁,称为“递归地”请求锁
- 在该线程释放锁之前,会对锁计数器进行累加操作,线程每成功获得一次锁时,都要进行相应的解锁操作,直到锁计数器清零才能完全释放该锁。
- 递归锁能够保证同一线程在持有锁时能够再次获取该锁,而不被自己所持有的锁所阻塞,从而避免死锁的发生。
- 但是注意要正常使用递归锁,避免过多地获取锁导致性能下降。
【1】死锁
from threading import Thread, Lock
import time
metexA = Lock()
metexB = Lock()
# 类只要加括号多次 产生的肯定不同的对象
# 如果你想要实现多次加括号等到的是相同的对象 - 单例模式
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
metexA.acquire()
# self.name:获取当前线程名
print(f'{self.name} 抢到了A锁')
metexB.acquire()
print(f'{self.name} 抢到了B锁')
metexB.release()
metexA.release()
def func2(self):
metexB.acquire()
# self.name:获取当前线程名
print(f'{self.name} 抢到了A锁')
time.sleep(2)
metexA.acquire()
print(f'{self.name} 抢到了B锁')
metexA.release()
metexB.release()
def main():
for i in range(10):
t = MyThread()
t.start()
if __name__ == '__main__':
main()
# Thread-1 抢到了A锁
# Thread-1 抢到了B锁
# Thread-1 抢到了A锁
# Thread-2 抢到了A锁
# 线程卡死
# 开启十个线程 第一个线程走完第一圈 回到原地抢 A 结果第二个线程已经拿到了A 导致AB卡死
【2】递归锁
可以被连续的 acquire 和 release
但是只能被第一个抢到这把锁上执行上述操作
他的内部有一个计数器,每acquire一次计数 +1 每release一次 计数-1
只要计数不为0,那么其他人都无法抢到该锁
from threading import Thread, Lock, RLock
import time
# 两个变量同时指向一把锁
metexA = metexB = RLock()
# 类只要加括号多次 产生的肯定不同的对象
# 如果你想要实现多次加括号等到的是相同的对象 - 单例模式
class MyThread(Thread):
def run(self):
self.func1()
self.func2()
def func1(self):
metexA.acquire()
# self.name:获取当前线程名
print(f'{self.name} 抢到了A锁')
metexB.acquire()
print(f'{self.name} 抢到了B锁')
metexB.release()
metexA.release()
def func2(self):
metexB.acquire()
# self.name:获取当前线程名
print(f'{self.name} 抢到了A锁')
time.sleep(2)
metexA.acquire()
print(f'{self.name} 抢到了B锁')
metexA.release()
metexB.release()
def main():
for i in range(10):
t = MyThread()
t.start()
if __name__ == '__main__':
main()
# Thread-1 抢到了A锁
# Thread-1 抢到了B锁
# Thread-1 抢到了A锁
# Thread-1 抢到了B锁
# Thread-2 抢到了A锁
# Thread-2 抢到了B锁
# Thread-2 抢到了A锁
# Thread-2 抢到了B锁
# Thread-4 抢到了A锁
# Thread-4 抢到了B锁
# Thread-4 抢到了A锁
# 不会卡主正常进行
【二】信号量
- 信号量在不同的阶段可能对应不同的技术点
- 在并发编程中信号量指的是锁
如果我们将互斥锁比喻成一个厕所
那信号量就相当于多个厕所
【1】基础版 1.0
通过信号量限制线程抢的线程开设数
线程数是5 , 只能每次5个人一起抢,一起走
# -*-coding: Utf-8 -*-
# @File : 02 信号量 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from threading import Thread, Semaphore
import time
import random
# 参数:表示开设几个空位
sm = Semaphore(5)
def task(name):
sm.acquire()
print(f'{name} 正在使用')
# 模拟延迟
# time.sleep(random.randint(1, 5))
time.sleep(3)
sm.release()
def main():
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
if __name__ == '__main__':
main()
# 0 正在使用
# 1 正在使用
# 2 正在使用
# 3 正在使用
# 4 正在使用
#
# 5 正在使用
# 7 正在使用
# 6 正在使用
# 9 正在使用
# 8 正在使用
# -*-coding: Utf-8 -*-
# @File : 02 信号量 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from threading import Thread, Semaphore
import time
import random
# 参数:表示开设几个空位
sm = Semaphore(5)
def task(name):
sm.acquire()
print(f'{name} 正在使用')
# 模拟延迟
time.sleep(random.randint(1, 5))
# time.sleep(3)
sm.release()
def main():
for i in range(20):
t = Thread(target=task, args=(f'{i}玩家',))
t.start()
if __name__ == '__main__':
main()
# 0 正在使用
# 1 正在使用
# 2 正在使用
# 3 正在使用
# 4 正在使用
#
# 5 正在使用
# 7 正在使用
# 6 正在使用
# 9 正在使用
# 8 正在使用
【三】Event事件
- 一些进程/线程需要等待另外一些进程/线程运行完毕后才能运行,类似于发射信号一样
# -*-coding: Utf-8 -*-
# @File : 03 Event事件 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from threading import Thread, Event
import time
# 造了一个红绿灯
event = Event()
# 设置灯
def light(name):
print(f'{name} 人:>>> red light')
time.sleep(2)
print(f'{name} 人:>>> green light')
# 告诉等待红灯的人可以走了
event.set()
def car(name):
print(f'{name} 车:>>> 正在等红灯')
# 等待别人的信号 :有人触发 .set() 这个方法 表示 .wait() 可以继续向下走
event.wait()
print(f'{name} 车:>>> 走了')
def main():
t = Thread(target=light, args=('dream',))
t.start()
for i in range(20):
t = Thread(target=car, args=('bud',))
t.start()
if __name__ == '__main__':
main()
# dream 人:>>> red light
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# bud 车:>>> 正在等红灯
# dream 人:>>> green light
# bud 车:>>> 走了
# bud 车:>>> 走了
# bud 车:>>> 走了bud 车:>>> 走了
# bud 车:>>> 走了bud 车:>>> 走了bud 车:>>> 走了
# bud 车:>>> 走了
# bud 车:>>> 走了
# bud 车:>>> 走了
# bud 车:>>> 走了
#
# bud 车:>>> 走了
# bud 车:>>> 走了
# bud 车:>>> 走了
#
#
# bud 车:>>> 走了
# bud 车:>>> 走了bud 车:>>> 走了
#
# bud 车:>>> 走了
# bud 车:>>> 走了
# bud 车:>>> 走了
【四】线程q
- 同一个进程下的多个线程数据是共享的
- 为什么在同一个进程下还要使用队列?
- 因为队列 = 管道 + 锁
- 使用队列是为了保证数据的安全
【1】后进先出 q
# -*-coding: Utf-8 -*-
# @File : 04 线程q .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
import queue
# 我们现在使用的队列都是只能在本地测试使用
# 队列q:先进先出
'''
# 队列的常用方法
q = queue.Queue(3)
q.put(1)
q.get()
q.get_nowait()
q.get(timeout=3)
q.full()
q.empty()
'''
# (1)后进先出q
q = queue.LifoQueue(3)
q.put(1)
q.put(2)
q.put(3)
print(q.get())
# 3
【2】PriorityQueue
设置队列优先级
数字越小优先级越高
import queue
# (2)优先级 q
# 可以给防止队列中的数据设置队列优先级
q = queue.PriorityQueue(3)
# (优先级,参数)
q.put((10, '111'))
q.put((100, '222'))
q.put((0, '333'))
q.put((-5, '444'))
print(q.get())
# (-5, '444')
# (优先级, 参数)
# 数字越小优先级越高
【五】进程池与线程池
【1】TCP实现并发的效果的原理
-
没开设一个客户端
- 就会有一个服务端服务
-
服务端
# -*-coding: Utf-8 -*-
# @File : 05 进程池与线程池 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from socket import *
from threading import Thread
def server_create(IP, PORT):
server = socket()
server.bind((IP, PORT))
server.listen(5)
while True:
conn, addr = server.accept()
t = Thread(target=conn_communication, args=(conn,))
def conn_communication(conn):
while True:
try:
data = conn.recv(1024)
if len(data) == 0:
break
print(data.decode('utf8'))
conn.send(data.upper())
except Exception as e:
print(e)
break
conn.close()
def main():
IP = '127.0.0.1'
PORT = 8086
t = Thread(target=server_create, args=(IP, PORT,))
t.start()
if __name__ == '__main__':
main()
- 客户端
# -*-coding: Utf-8 -*-
# @File : 05 进程池与线程池 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from socket import *
def client_create(IP, PORT):
client = socket()
client.connect((IP, PORT))
while True:
word = b'hello world'
client.send(word)
msg = client.recv(1024)
print(msg)
if __name__ == '__main__':
IP = '127.0.0.1'
PORT = 8086
client_create(IP, PORT)
无论是开设进程还是开设线程,都需要消耗资源
只不过开始线程消耗的资源比开始进程相对少一些
硬件的开发速度永远赶不上软件开发速度
我们的宗旨是保证计算机硬件正常工作的情况下最大程度的利用它
【2】池的概念
【1】什么是池?
- 池是用来保证计算机硬件安全的情况下最大限度的利用计算机
- 池降低了程序的运行效率,但是保证了计算机硬件的安全,从而保证程序的正常运行
【2】基础版 2.0
# -*-coding: Utf-8 -*-
# @File : 06 进程池和线程池 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time
# (1)线程池
# (1.1)参数详解
# 默认开设当前计算机 cpu 个数五倍数的线程数:可指定线程数
# (1.2)原理详解
# 池子造出来后 里面固定存在五个线程
# 这五个线程不会存在出现重复创建和销毁的过程
# (1.3)优点
# 避免了重复创建五个线程的资源开销
# (1.4)使用方法
# 构造好池子以后,只需要将需要做的任务向池子中提交即可
pool = ThreadPoolExecutor(5)
def task(n):
print(n)
time.sleep(2)
# 向池子中提交任务
'''
任务的提交方式
同步:提交任务之后原地等待任务的返回结果,期间不做任何事
异步:提交任务之后不等待任务的返回结果,继续执行代码
返回结果如何获取?
'''
def normal():
pool.submit(task, 1)
# 异步提交 - 先执行子线程再打印主线程
print('this is a main task')
# 1
# this is a main task
if __name__ == '__main__':
normal()
【3】升级版 3.0
# -*-coding: Utf-8 -*-
# @File : 06 进程池和线程池 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time
# (1)线程池
# (1.1)参数详解
# 默认开设当前计算机 cpu 个数五倍数的线程数:可指定线程数
# (1.2)原理详解
# 池子造出来后 里面固定存在五个线程
# 这五个线程不会存在出现重复创建和销毁的过程
# (1.3)优点
# 避免了重复创建五个线程的资源开销
# (1.4)使用方法
# 构造好池子以后,只需要将需要做的任务向池子中提交即可
pool = ThreadPoolExecutor(5)
def task(n):
print(n)
time.sleep(2)
# 向池子中提交任务
'''
任务的提交方式
同步:提交任务之后原地等待任务的返回结果,期间不做任何事
异步:提交任务之后不等待任务的返回结果,继续执行代码
返回结果如何获取?
'''
def normal():
pool.submit(task, 1)
# 异步提交 - 先执行子线程再打印主线程
print('this is a main task')
# 1
# this is a main task
def many_steps():
for i in range(20):
pool.submit(task, i)
print('this is a main task')
# 0
# 1
# 2
# 3
# 4
# this is a main task
# 先执行五个子线程,再回到主线程
if __name__ == '__main__':
many_steps()
【4】进阶版 4.0
# -*-coding: Utf-8 -*-
# @File : 06 进程池和线程池 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time
# (1)线程池
# (1.1)参数详解
# 默认开设当前计算机 cpu 个数五倍数的线程数:可指定线程数
# (1.2)原理详解
# 池子造出来后 里面固定存在五个线程
# 这五个线程不会存在出现重复创建和销毁的过程
# (1.3)优点
# 避免了重复创建五个线程的资源开销
# (1.4)使用方法
# 构造好池子以后,只需要将需要做的任务向池子中提交即可
pool = ThreadPoolExecutor(5)
def task(n):
print(n)
time.sleep(2)
# 向池子中提交任务
'''
任务的提交方式
同步:提交任务之后原地等待任务的返回结果,期间不做任何事
异步:提交任务之后不等待任务的返回结果,继续执行代码
返回结果如何获取?
'''
def normal():
pool.submit(task, 1)
# 异步提交 - 先执行子线程再打印主线程
print('this is a main task')
# 1
# this is a main task
def many_steps():
for i in range(20):
res = pool.submit(task, i)
print(f'这是 pool的返回值:>>>>{res}')
# 返回值是一个类对象 - result 方法 ----> 返回异步回调的结果
print(f'这是 pool的返回值:>>>>{res.result()}') # None
# 程序由并发变成了串行
# 返回的结果是 None ---> 任务的返回结果 ----> 拿到了异步提交的返回结果
# res.result() : 拿到的就是异步提交的任务的返回结果
print('this is a main task')
# 0这是 pool的返回值:>>>><Future at 0x1767f648a60 state=running>
#
# 1这是 pool的返回值:>>>><Future at 0x1767f7bce50 state=running>
#
# 2
# 这是 pool的返回值:>>>><Future at 0x1767f7da220 state=running>
# 3
# 这是 pool的返回值:>>>><Future at 0x1767f7da5b0 state=running>
# 4
# 这是 pool的返回值:>>>><Future at 0x1767f7da940 state=running>
# 这是 pool的返回值:>>>><Future at 0x1767f7dad30 state=pending>
# 这是 pool的返回值:>>>><Future at 0x1767f7dae50 state=pending>
# 这是 pool的返回值:>>>><Future at 0x1767f7daf70 state=pending>
# 这是 pool的返回值:>>>><Future at 0x1767f9e10d0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x1767f9e11f0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x1767f9e1310 state=pending>
# 这是 pool的返回值:>>>><Future at 0x1767f9e1430 state=pending>
# 这是 pool的返回值:>>>><Future at 0x1767f9e1550 state=pending>
# 这是 pool的返回值:>>>><Future at 0x1767f9e1670 state=pending>
# 这是 pool的返回值:>>>><Future at 0x1767f9e1790 state=pending>
# 这是 pool的返回值:>>>><Future at 0x1767f9e18b0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x1767f9e19d0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x1767f9e1af0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x1767f9e1c10 state=pending>
# 这是 pool的返回值:>>>><Future at 0x1767f9e1d30 state=pending>
# this is a main task
if __name__ == '__main__':
many_steps()
【5】迭代版 5.0
# -*-coding: Utf-8 -*-
# @File : 06 进程池和线程池 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time
# (1)线程池
# (1.1)参数详解
# 默认开设当前计算机 cpu 个数五倍数的线程数:可指定线程数
# (1.2)原理详解
# 池子造出来后 里面固定存在五个线程
# 这五个线程不会存在出现重复创建和销毁的过程
# (1.3)优点
# 避免了重复创建五个线程的资源开销
# (1.4)使用方法
# 构造好池子以后,只需要将需要做的任务向池子中提交即可
pool = ThreadPoolExecutor(5)
def task(n):
print(n)
time.sleep(2)
# 向池子中提交任务
'''
任务的提交方式
同步:提交任务之后原地等待任务的返回结果,期间不做任何事
异步:提交任务之后不等待任务的返回结果,继续执行代码
返回结果如何获取?
'''
def normal():
pool.submit(task, 1)
# 异步提交 - 先执行子线程再打印主线程
print('this is a main task')
# 1
# this is a main task
def many_steps():
# 创建任务池子
res_list = []
for i in range(20):
res = pool.submit(task, i)
print(f'这是 pool的返回值:>>>>{res}')
# 返回值是一个类对象 - result 方法 ----> 返回异步回调的结果
res_list.append(res)
for res in res_list:
print(f'这是 pool的返回值:>>>>{res.result()}') # None
# 程序由变成了串行
# res.result() : 拿到的就是异步提交的任务的返回结果
# 程序运行的结果是有序的
print('this is a main task')
# 0这是 pool的返回值:>>>><Future at 0x13bb6c68cd0 state=running>
#
# 1
# 这是 pool的返回值:>>>><Future at 0x13bb6df9100 state=running>
# 2
# 这是 pool的返回值:>>>><Future at 0x13bb6df9490 state=running>
# 3
# 这是 pool的返回值:>>>><Future at 0x13bb6df9820 state=running>
# 4
# 这是 pool的返回值:>>>><Future at 0x13bb6df9bb0 state=running>
# 这是 pool的返回值:>>>><Future at 0x13bb6df9f70 state=pending>
# 这是 pool的返回值:>>>><Future at 0x13bb6dff100 state=pending>
# 这是 pool的返回值:>>>><Future at 0x13bb6dff220 state=pending>
# 这是 pool的返回值:>>>><Future at 0x13bb6dff340 state=pending>
# 这是 pool的返回值:>>>><Future at 0x13bb6dff460 state=pending>
# 这是 pool的返回值:>>>><Future at 0x13bb6dff580 state=pending>
# 这是 pool的返回值:>>>><Future at 0x13bb6dff6a0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x13bb6dff7c0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x13bb6dff8e0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x13bb6dffa00 state=pending>
# 这是 pool的返回值:>>>><Future at 0x13bb6dffb20 state=pending>
# 这是 pool的返回值:>>>><Future at 0x13bb6dffc40 state=pending>
# 这是 pool的返回值:>>>><Future at 0x13bb6dffd60 state=pending>
# 这是 pool的返回值:>>>><Future at 0x13bb6dffe80 state=pending>
# 这是 pool的返回值:>>>><Future at 0x13bb6dfffa0 state=pending>
# 56
# 7
# 8
# 9
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
#
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 101112
# 13
# 14
#
#
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 1516
# 17
# 18
#
# 19
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# this is a main task
if __name__ == '__main__':
many_steps()
【6】增强版 6.0 - pool.shutdown()
等待所有子线程结束后再打印程序运行结果
# -*-coding: Utf-8 -*-
# @File : 06 进程池和线程池 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time
# (1)线程池
# (1.1)参数详解
# 默认开设当前计算机 cpu 个数五倍数的线程数:可指定线程数
# (1.2)原理详解
# 池子造出来后 里面固定存在五个线程
# 这五个线程不会存在出现重复创建和销毁的过程
# (1.3)优点
# 避免了重复创建五个线程的资源开销
# (1.4)使用方法
# 构造好池子以后,只需要将需要做的任务向池子中提交即可
pool = ThreadPoolExecutor(5)
def task(n):
print(n)
time.sleep(2)
return n*n
# 向池子中提交任务
'''
任务的提交方式
同步:提交任务之后原地等待任务的返回结果,期间不做任何事
异步:提交任务之后不等待任务的返回结果,继续执行代码
返回结果如何获取?
'''
def normal():
pool.submit(task, 1)
# 异步提交 - 先执行子线程再打印主线程
print('this is a main task')
# 1
# this is a main task
def many_steps():
# 创建任务池子
res_list = []
for i in range(20):
res = pool.submit(task, i)
print(f'这是 pool的返回值:>>>>{res}')
# 返回值是一个类对象 - result 方法 ----> 返回异步回调的结果
res_list.append(res)
# 关闭线程池 - 等待所有的线程池中的任务运行完毕
pool.shutdown()
for res in res_list:
print(f'这是 pool的返回值:>>>>{res.result()}') # None
# 程序由变成了串行
# res.result() : 拿到的就是异步提交的任务的返回结果
# 程序运行的结果是有序的
print('this is a main task')
# 1
# 这是 pool的返回值:>>>><Future at 0x29bc36d91c0 state=running>
# 2这是 pool的返回值:>>>><Future at 0x29bc36d9550 state=running>
#
# 3
# 这是 pool的返回值:>>>><Future at 0x29bc36d98e0 state=running>
# 4
# 这是 pool的返回值:>>>><Future at 0x29bc36d9c70 state=running>
# 这是 pool的返回值:>>>><Future at 0x29bc36de070 state=pending>
# 这是 pool的返回值:>>>><Future at 0x29bc36de1c0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x29bc36de2e0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x29bc36de400 state=pending>
# 这是 pool的返回值:>>>><Future at 0x29bc36de520 state=pending>
# 这是 pool的返回值:>>>><Future at 0x29bc36de640 state=pending>
# 这是 pool的返回值:>>>><Future at 0x29bc36de760 state=pending>
# 这是 pool的返回值:>>>><Future at 0x29bc36de880 state=pending>
# 这是 pool的返回值:>>>><Future at 0x29bc36de9a0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x29bc36deac0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x29bc36debe0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x29bc36ded00 state=pending>
# 这是 pool的返回值:>>>><Future at 0x29bc36dee20 state=pending>
# 这是 pool的返回值:>>>><Future at 0x29bc36def40 state=pending>
# 这是 pool的返回值:>>>><Future at 0x29bc36e50a0 state=pending>
# 5
# 67
#
# 8
# 9
# 10
# 1112
# 13
#
# 14
# 15
# 1617
# 1819
#
#
# 这是 pool的返回值:>>>>0
# 这是 pool的返回值:>>>>1
# 这是 pool的返回值:>>>>4
# 这是 pool的返回值:>>>>9
# 这是 pool的返回值:>>>>16
# 这是 pool的返回值:>>>>25
# 这是 pool的返回值:>>>>36
# 这是 pool的返回值:>>>>49
# 这是 pool的返回值:>>>>64
# 这是 pool的返回值:>>>>81
# 这是 pool的返回值:>>>>100
# 这是 pool的返回值:>>>>121
# 这是 pool的返回值:>>>>144
# 这是 pool的返回值:>>>>169
# 这是 pool的返回值:>>>>196
# 这是 pool的返回值:>>>>225
# 这是 pool的返回值:>>>>256
# 这是 pool的返回值:>>>>289
# 这是 pool的返回值:>>>>324
# 这是 pool的返回值:>>>>361
# this is a main task
if __name__ == '__main__':
many_steps()
【3】进程池的使用
【1】基础版 1.0
开设进程的进程 ID 号不会发生改变
# -*-coding: Utf-8 -*-
# @File : 06 进程池和线程池 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time, os
# (1)线程池
# (1.1)参数详解
# 默认开设当前计算机 cpu 个数 的进程 : 可指定进程数
# (1.2)原理详解
# 池子造出来后 里面固定存在五个进程
# 这五个进程不会存在出现重复创建和销毁的过程
# (1.3)优点
# 避免了重复创建五个进程的资源开销
# (1.4)使用方法
# 构造好池子以后,只需要将需要做的任务向池子中提交即可
pool = ProcessPoolExecutor(5)
def task(n):
print(n, f'当前进程的进程号:>>>{os.getpid()}')
time.sleep(2)
return n * n
# 向池子中提交任务
'''
任务的提交方式
同步:提交任务之后原地等待任务的返回结果,期间不做任何事
异步:提交任务之后不等待任务的返回结果,继续执行代码
返回结果如何获取?
'''
def normal():
pool.submit(task, 1)
# 异步提交 - 先执行子线程再打印主线程
print('this is a main task ')
# 1
# this is a main task
def many_steps():
# 创建任务池子
res_list = []
for i in range(20):
res = pool.submit(task, i)
print(f'这是 pool的返回值:>>>>{res}')
# 返回值是一个类对象 - result 方法 ----> 返回异步回调的结果
res_list.append(res)
# 关闭线程池 - 等待所有的线程池中的任务运行完毕
pool.shutdown()
for res in res_list:
print(f'这是 pool的返回值:>>>>{res.result()}') # None
# 程序由变成了串行
# res.result() : 拿到的就是异步提交的任务的返回结果
# 程序运行的结果是有序的
print('this is a main task')
# 这是 pool的返回值:>>>><Future at 0x23ca907d460 state=running>
# 这是 pool的返回值:>>>><Future at 0x23ca928b550 state=running>
# 这是 pool的返回值:>>>><Future at 0x23ca92f6af0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca92f65e0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca92f6940 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca92f6a00 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca92f6b20 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca92f6ca0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca92f6d30 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca92f6e20 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca92f6f70 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca92f6f40 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca9301220 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca9301340 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca9301310 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca9301550 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca9301670 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca9301760 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca93018b0 state=pending>
# 这是 pool的返回值:>>>><Future at 0x23ca9301a00 state=pending>
# 0 当前进程的进程号:>>>14680
# 1 当前进程的进程号:>>>17936
# 2 当前进程的进程号:>>>6028
# 3 当前进程的进程号:>>>18332
# 4 当前进程的进程号:>>>19176
# 5 当前进程的进程号:>>>14680
# 6 当前进程的进程号:>>>17936
# 7 当前进程的进程号:>>>6028
# 8 当前进程的进程号:>>>18332
# 9 当前进程的进程号:>>>19176
# 10 当前进程的进程号:>>>14680
# 11 当前进程的进程号:>>>17936
# 12 当前进程的进程号:>>>6028
# 13 当前进程的进程号:>>>18332
# 14 当前进程的进程号:>>>19176
# 15 当前进程的进程号:>>>14680
# 16 当前进程的进程号:>>>17936
# 17 当前进程的进程号:>>>6028
# 18 当前进程的进程号:>>>18332
# 19 当前进程的进程号:>>>19176
# 这是 pool的返回值:>>>>0
# 这是 pool的返回值:>>>>1
# 这是 pool的返回值:>>>>4
# 这是 pool的返回值:>>>>9
# 这是 pool的返回值:>>>>16
# 这是 pool的返回值:>>>>25
# 这是 pool的返回值:>>>>36
# 这是 pool的返回值:>>>>49
# 这是 pool的返回值:>>>>64
# 这是 pool的返回值:>>>>81
# 这是 pool的返回值:>>>>100
# 这是 pool的返回值:>>>>121
# 这是 pool的返回值:>>>>144
# 这是 pool的返回值:>>>>169
# 这是 pool的返回值:>>>>196
# 这是 pool的返回值:>>>>225
# 这是 pool的返回值:>>>>256
# 这是 pool的返回值:>>>>289
# 这是 pool的返回值:>>>>324
# 这是 pool的返回值:>>>>361
# this is a main task
if __name__ == '__main__':
many_steps()
【2】进阶版 2.0 - add_done_callback(call_back)
add_done_callback(call_back) 返回值正是我们的上面 pool的返回的结果
# -*-coding: Utf-8 -*-
# @File : 06 进程池和线程池 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time, os
# (1)线程池
# (1.1)参数详解
# 默认开设当前计算机 cpu 个数 的进程 : 可指定进程数
# (1.2)原理详解
# 池子造出来后 里面固定存在五个进程
# 这五个进程不会存在出现重复创建和销毁的过程
# (1.3)优点
# 避免了重复创建五个进程的资源开销
# (1.4)使用方法
# 构造好池子以后,只需要将需要做的任务向池子中提交即可
pool = ProcessPoolExecutor(5)
def task(n):
print(n, f'当前进程的进程号:>>>{os.getpid()}')
time.sleep(2)
return n * n
# 向池子中提交任务
'''
任务的提交方式
同步:提交任务之后原地等待任务的返回结果,期间不做任何事
异步:提交任务之后不等待任务的返回结果,继续执行代码
返回结果如何获取?
异步提交任务的返回结果 应该通过回调机制来获取
异步回调机制?
就相当于给每个异步任务绑定了一个定时炸弹
一旦任务有结果会立即触发爆炸
'''
def call_back(n):
print(f'call_back>>>:{n}')
def normal():
pool.submit(task, 1)
# 异步提交 - 先执行子线程再打印主线程
print('this is a main task ')
# 1
# this is a main task
def many_steps():
# 创建任务池子
res_list = []
for i in range(20):
# 继续添加异步回调方法
res = pool.submit(task, i).add_done_callback(call_back)
print(f'这是 pool的返回值:>>>>{res}')
# 返回值是一个类对象 - result 方法 ----> 返回异步回调的结果
# res_list.append(res)
"""
# 关闭线程池 - 等待所有的线程池中的任务运行完毕
pool.shutdown()
for res in res_list:
print(f'这是 pool的返回值:>>>>{res.result()}') # None
# 程序由变成了串行
# res.result() : 拿到的就是异步提交的任务的返回结果
# 程序运行的结果是有序的
print('this is a main task')
"""
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 0 当前进程的进程号:>>>17524
# 1 当前进程的进程号:>>>10136
# 2 当前进程的进程号:>>>5420
# 3 当前进程的进程号:>>>3932
# 4 当前进程的进程号:>>>17644
# call_back>>>:<Future at 0x27b365622e0 state=finished returned int>
# 5 当前进程的进程号:>>>17524
# 6 当前进程的进程号:>>>5420
# call_back>>>:<Future at 0x27b367db370 state=finished returned int>
# 7 当前进程的进程号:>>>10136
# call_back>>>:<Future at 0x27b3656d3d0 state=finished returned int>
# 8 当前进程的进程号:>>>3932
# call_back>>>:<Future at 0x27b367db610 state=finished returned int>
# 9 当前进程的进程号:>>>17644
# call_back>>>:<Future at 0x27b367db760 state=finished returned int>
# 10 当前进程的进程号:>>>17524
# call_back>>>:<Future at 0x27b367db4f0 state=finished returned int>
# 1211 当前进程的进程号:>>>5420当前进程的进程号:>>>10136
#
# call_back>>>:<Future at 0x27b367db910 state=finished returned int>
# call_back>>>:<Future at 0x27b367dbac0 state=finished returned int>
# 13 当前进程的进程号:>>>3932
# call_back>>>:<Future at 0x27b367dbbb0 state=finished returned int>
# call_back>>>:<Future at 0x27b367dbca0 state=finished returned int>
# 14 当前进程的进程号:>>>17644
# 15 当前进程的进程号:>>>17524
# call_back>>>:<Future at 0x27b367dbdf0 state=finished returned int>
# 16 当前进程的进程号:>>>5420
# 17 当前进程的进程号:>>>10136
# call_back>>>:<Future at 0x27b367e2070 state=finished returned int>
# call_back>>>:<Future at 0x27b367dbf10 state=finished returned int>
# 18 当前进程的进程号:>>>3932
# call_back>>>:<Future at 0x27b367e2190 state=finished returned int>
# 19 当前进程的进程号:>>>17644
# call_back>>>:<Future at 0x27b367e22b0 state=finished returned int>
# call_back>>>:<Future at 0x27b367e23d0 state=finished returned int>
# call_back>>>:<Future at 0x27b367e24f0 state=finished returned int>
# call_back>>>:<Future at 0x27b367e2610 state=finished returned int>
# call_back>>>:<Future at 0x27b367e2730 state=finished returned int>
# call_back>>>:<Future at 0x27b367e2850 state=finished returned int>
#
# Process finished with exit code 0
if __name__ == '__main__':
many_steps()
【3】升级版 - 3.0
# -*-coding: Utf-8 -*-
# @File : 06 进程池和线程池 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time, os
# (1)线程池
# (1.1)参数详解
# 默认开设当前计算机 cpu 个数 的进程 : 可指定进程数
# (1.2)原理详解
# 池子造出来后 里面固定存在五个进程
# 这五个进程不会存在出现重复创建和销毁的过程
# (1.3)优点
# 避免了重复创建五个进程的资源开销
# (1.4)使用方法
# 构造好池子以后,只需要将需要做的任务向池子中提交即可
pool = ProcessPoolExecutor(5)
def task(n):
print(n, f'当前进程的进程号:>>>{os.getpid()}')
time.sleep(2)
return n * n
# 向池子中提交任务
'''
任务的提交方式
同步:提交任务之后原地等待任务的返回结果,期间不做任何事
异步:提交任务之后不等待任务的返回结果,继续执行代码
返回结果如何获取?
异步提交任务的返回结果 应该通过回调机制来获取
异步回调机制?
就相当于给每个异步任务绑定了一个定时炸弹
一旦任务有结果会立即触发爆炸
'''
def call_back(n):
# 这里的n是对象
print(f'call_back>>>:{n}返回的结果{n.result()}')
def normal():
pool.submit(task, 1)
# 异步提交 - 先执行子线程再打印主线程
print('this is a main task ')
# 1
# this is a main task
def many_steps():
# 创建任务池子
res_list = []
for i in range(20):
# 继续添加异步回调方法
res = pool.submit(task, i).add_done_callback(call_back)
print(f'这是 pool的返回值:>>>>{res}')
# 返回值是一个类对象 - result 方法 ----> 返回异步回调的结果
# res_list.append(res)
"""
# 关闭线程池 - 等待所有的线程池中的任务运行完毕
pool.shutdown()
for res in res_list:
print(f'这是 pool的返回值:>>>>{res.result()}') # None
# 程序由变成了串行
# res.result() : 拿到的就是异步提交的任务的返回结果
# 程序运行的结果是有序的
print('this is a main task')
"""
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 这是 pool的返回值:>>>>None
# 0 当前进程的进程号:>>>11484
# 1 当前进程的进程号:>>>5068
# 2 当前进程的进程号:>>>20224
# 3 当前进程的进程号:>>>16524
# 4 当前进程的进程号:>>>14784
# 5 当前进程的进程号:>>>11484
# call_back>>>:<Future at 0x1c2f98322e0 state=finished returned int>返回的结果0
# 6 当前进程的进程号:>>>5068
# call_back>>>:<Future at 0x1c2f983d3d0 state=finished returned int>返回的结果1
# 7 当前进程的进程号:>>>20224
# 8 当前进程的进程号:>>>16524
# call_back>>>:<Future at 0x1c2f98ab370 state=finished returned int>返回的结果9
# call_back>>>:<Future at 0x1c2f98ab3a0 state=finished returned int>返回的结果4
# 9 当前进程的进程号:>>>14784
# call_back>>>:<Future at 0x1c2f98ab760 state=finished returned int>返回的结果16
# 10 当前进程的进程号:>>>11484
# 11 当前进程的进程号:>>>5068
# call_back>>>:<Future at 0x1c2f98ab580 state=finished returned int>返回的结果25
# call_back>>>:<Future at 0x1c2f98ab910 state=finished returned int>返回的结果36
# 12 当前进程的进程号:>>>20224
# 13 当前进程的进程号:>>>16524
# call_back>>>:<Future at 0x1c2f98ab940 state=finished returned int>返回的结果49
# call_back>>>:<Future at 0x1c2f98abbb0 state=finished returned int>返回的结果64
# 14 当前进程的进程号:>>>14784
# call_back>>>:<Future at 0x1c2f98abd00 state=finished returned int>返回的结果81
# 15 当前进程的进程号:>>>11484
# 16 当前进程的进程号:>>>5068
# call_back>>>:<Future at 0x1c2f98abdf0 state=finished returned int>返回的结果100
# call_back>>>:<Future at 0x1c2f98abf40 state=finished returned int>返回的结果121
# 17 当前进程的进程号:>>>16524
# 18 当前进程的进程号:>>>20224
# call_back>>>:<Future at 0x1c2f98b2070 state=finished returned int>返回的结果144
# call_back>>>:<Future at 0x1c2f98b21c0 state=finished returned int>返回的结果169
# 19 当前进程的进程号:>>>14784
# call_back>>>:<Future at 0x1c2f98b22b0 state=finished returned int>返回的结果196
# call_back>>>:<Future at 0x1c2f98b24f0 state=finished returned int>返回的结果256
# call_back>>>:<Future at 0x1c2f98b23d0 state=finished returned int>返回的结果225
# call_back>>>:<Future at 0x1c2f98b2700 state=finished returned int>返回的结果324
# call_back>>>:<Future at 0x1c2f98b25e0 state=finished returned int>返回的结果289
# call_back>>>:<Future at 0x1c2f98b2850 state=finished returned int>返回的结果361
#
# Process finished with exit code 0
if __name__ == '__main__':
many_steps()
【4】总结
# 导入模块
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
# 构建池子
pool = ProcessPoolExecutor(5)
# 提交任务
pool.submit(task, i).add_done_callback(call_back)
【六】协程
-
进程
- 资源单位
-
线程
- 执行单位
-
协程
- 假想存在概念:单线程下实现并发的效果
- 在代码层面上监控所有的 IO 操作,一旦遇到 IO 就在代码层级完成切换
- 让CPU一直运行,没有IO,从而提升程序的运行效率
-
多道技术
-
切换 + 并发状态
-
切换不一定是提升效率,也可能是降低效率
-
IO 切换:提升效率
-
无IO 切换:降低效率
-
-
代码如何实现?
- 保存上一次的执行状态,下一次继续执行
- yield 方法(保存状态)
- 保存上一次的执行状态,下一次继续执行
-
-
CPU切换的状态
- 程序遇到IO
- 程序长时间占用
【1】验证切换是否一定提升效率?
【1】基础版 - 1.0
最原始的计算方式:串行
# -*-coding: Utf-8 -*-
# @File : 07 协程 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
import time
def func1():
for i in range(100000000):
i += 1
def func2():
for i in range(100000000):
i += 1
def normal():
func1()
func2()
def main():
start_time = time.time()
normal()
print(f'总耗时{time.time() - start_time}')
# 总耗时10.284734964370728
if __name__ == '__main__':
main()
【2】升级版 - 2.0
切换 + yield --- 时间变长
# -*-coding: Utf-8 -*-
# @File : 07 协程 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
import time
def func1():
while True:
100000000 + 1
yield
def func2():
# 初识化迭代生成器
g = func1()
for i in range(100000000):
i + 1
# 调用迭代生成器
next(g)
def normal():
func1()
func2()
def theGenerator():
func2()
def main():
start_time = time.time()
theGenerator()
print(f'总耗时{time.time() - start_time}')
# 总耗时15.642895460128784
if __name__ == '__main__':
main()
【3】增强版 - 3.0 - gevent模块
- 基础版 - 单线程操作
# -*-coding: Utf-8 -*-
# @File : 07 协程 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
import time
import gevent
def hello_me():
print(f'hello you')
time.sleep(3)
print(f'hello you')
def hello_you():
print(f'hello me')
time.sleep(2)
print(f'hello me')
def run():
hello_me()
hello_you()
def main():
start_time = time.time()
run()
print(f'总耗时{time.time() - start_time}')
# hello you
# hello you
# hello me
# hello me
# 总耗时5.00632119178772
if __name__ == '__main__':
main()
- 升级版 - 多线程 - 切换 IO
程序运行的时间取自运行时间最长的那个
# -*-coding: Utf-8 -*-
# @File : 07 协程 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
import time
from gevent import spawn
from gevent import monkey
monkey.patch_all()
'''
gevent : 无法检测常见的 IO 操作,只能检测自己的 IO 操作
如果想检测 其他 IO 需要加上一句
from gevent import monkey
monkey.patch_all()
又由于上面的代码在使用 gevent 模块的时候是必须导入的,所以支持简写
from gevent import monkey;monkey.patch_all()
'''
def hello_me():
print(f'hello you')
time.sleep(3)
print(f'hello you')
def hello_you():
print(f'hello me')
time.sleep(2)
print(f'hello me')
def run():
g1 = spawn(hello_me)
g2 = spawn(hello_you)
# 等待被检测的任务执行完毕再向下进行
g1.join()
g2.join()
def main():
start_time = time.time()
run()
print(f'总耗时{time.time() - start_time}')
# 当程序遇到 IO 时 会立马切换到没有 IO 的进程上
# hello you
# hello me
# hello me
# hello you
# 总耗时3.0524613857269287
if __name__ == '__main__':
main()
【2】协程实现 TCP 高并发效果
- 服务端
# -*-coding: Utf-8 -*-
# @File : 05 进程池与线程池 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from socket import *
from threading import Thread
from gevent import monkey
monkey.patch_all()
from gevent import spawn
def server_create(IP, PORT):
server = socket()
server.bind((IP, PORT))
server.listen(5)
while True:
conn, addr = server.accept()
spawn(conn_communication, conn)
def conn_communication(conn):
while True:
try:
data = conn.recv(1024)
if len(data) == 0:
break
print(data.decode('utf8'))
conn.send(data.upper())
except Exception as e:
print(e)
break
conn.close()
def main():
IP = '127.0.0.1'
PORT = 8086
g1 = spawn(server_create, IP, PORT)
g1.join()
if __name__ == '__main__':
main()
- 客户端
# -*-coding: Utf-8 -*-
# @File : 05 进程池与线程池 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from socket import *
from threading import Thread, current_thread
def client_create(IP, PORT):
count = 0
client = socket()
client.connect((IP, PORT))
while True:
msg = f'{count} is running {current_thread().name}'
count += 1
client.send(msg.encode('utf8'))
msg = client.recv(1024)
print(msg.decode('utf8'))
def main():
IP = '127.0.0.1'
PORT = 8086
for i in range(100)
t = Thread(target=client_create, args=(IP, PORT,))
t.start()
if __name__ == '__main__':
main()
【七】总结
- 我们可以通过多进程下面开设多线程
- 再通过多线程下面开设协程
- 达到提升程序的执行效率
本文来自博客园,作者:Chimengmeng,转载请注明原文链接:https://www.cnblogs.com/dream-ze/p/17506349.html