线程进阶知识点小结
9449 字
47 分钟
线程进阶知识点小结
【4.0】知识点小结(线程进阶)
【一】什么是死锁与递归锁
- 死锁是指两个或多个进程,在执行过程中,因争夺资源而造成了互相等待的一种现象。
- 即两个或多个进程持有各自的锁并试图获取对方持有的锁,从而导致被阻塞,不能向前执行,最终形成僵局。
- 在这种情况下,系统资源利用率极低,系统处于一种死循环状态。
- 递归锁(也叫可重入锁)是一种特殊的锁,它允许一个线程多次请求同一个锁,称为“递归地”请求锁
- 在该线程释放锁之前,会对锁计数器进行累加操作,线程每成功获得一次锁时,都要进行相应的解锁操作,直到锁计数器清零才能完全释放该锁。
- 递归锁能够保证同一线程在持有锁时能够再次获取该锁,而不被自己所持有的锁所阻塞,从而避免死锁的发生。
- 但是注意要正常使用递归锁,避免过多地获取锁导致性能下降。
【1】死锁
from threading import Thread, Lockimport 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, RLockimport 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/26from threading import Thread, Semaphoreimport timeimport 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/26from threading import Thread, Semaphoreimport timeimport 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/26from threading import Thread, Eventimport 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)后进先出qq = 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/26from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutorimport 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/26from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutorimport 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/26from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutorimport 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/26from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutorimport 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/26from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutorimport 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/26from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutorimport 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/26from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutorimport 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/26from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutorimport 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/26import 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/26import 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/26import timeimport 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/26import timefrom gevent import spawnfrom gevent import monkey
monkey.patch_all()
'''gevent : 无法检测常见的 IO 操作,只能检测自己的 IO 操作
如果想检测 其他 IO 需要加上一句from gevent import monkeymonkey.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 Threadfrom 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()【七】总结
- 我们可以通过多进程下面开设多线程
- 再通过多线程下面开设协程
- 达到提升程序的执行效率
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
相关文章智能推荐
1
C 语言发展史
编程语言了解 C 语言从 BCPL、B 语言到现代标准 C 的发展历程、核心特点与应用领域。
2
C 语言开发环境的安装与配置
编程语言在 Windows、Linux 与 macOS 上安装 C 工具链,配置编辑器,并编译运行第一个 C 程序。
3
C 语言初识:程序运行机制与基本结构
编程语言认识 C 程序从源代码到运行的完整流程,以及基础语法和程序的基本结构。
4
C 语言基础:注释、关键字、变量与数据类型
编程语言学习 C 语言的注释、关键字、标识符、常量、变量和基本数据类型等基础知识。
5
C 语言数据类型
编程语言理解 C 语言的数据类型体系,包括基本类型、整数类型、浮点类型和逻辑类型。
随机文章随机推荐














