操作系统发展史 穿孔卡片 一个计算机房,只能被一个穿孔卡片使用
缺点:
联机批处理 支持多用户去使用一个计算机房
脱机批处理系统 高速磁盘:
优点:
多道技术(基于单核情况下研究)
单道:多个使用CPU是串行
多道:
空间上的复用:一个CPU可以提供给多个用户去使用
时间上的复用:切换 + 保存状态
时间上复用说明:
(1)若CPU遇到IO操作,会立即将当前执行程序CPU使用权断开 优点:CPU的利用率高
(2)若一个程序使用CPU的时间过长,会立即将当前执行程序CPU使用权断开
缺点:程序的执行率降低
并发与并行 并发:指的是看起来像同时运行,多个程序不停地切换 + 保存状态
并行:真实意义上的同时运行
进程 程序与进程:
进程调度: 当代操作系统使用的是:时间片轮转法 + 分级反馈队列
1、先来先服务调度: a, b程序, 若a程序先来, 先占用CPU
缺点:程序a先使用,程序b必须等待程序a使用CPU结束后才能使用
2、短作业优先调度: a, b程序,谁的用时短,先优先调度使用cpu
缺点:若程序a使用时间最长,有N个程序使用时间短,必须等待所有用时短的程序结束后才能使用
3、时间片轮转法 CPU执行的时间1秒中,加载N个程序。要将1秒钟等分N个时间片
4、分级反馈队列 将执行优先分为多层级别
同步异步阻塞非阻塞 进程的三个状态:
就绪态:所有程序创建时都会进入就绪态,准备调度
运行态:调度后的进程,进入运行态
阻塞态:凡是遇到IO操作的进程,都会进入阻塞态;若IO结束必须重新进入就绪态
同步与异步 指的是提交任务的方式
同步:若有两个任务需要提交,在提交第一个任务时,必须等待该任务执行结束后,才能继续提交并执行第二个任务。
异步:若有两个任务需要提交,在提交第一个任务时,不需要原地等待,立即可以提交并执行第二个任务。
阻塞与非阻塞
阻塞:遇到IO一定会阻塞
非阻塞:指的是就绪态、运行态
最大化提高CPU的使用率:尽可能减少不必要的IO操作
创建进程 进程的创建 windows中创建子进程,会将当前父进程代码重新加载执行一次
在Linux/mac中,会将当前父进程重新拷贝一份,再去执行
创建子进程两种方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 from multiprocessing import Processimport timedef task (name ): print (f'{name} 的任务开始执行' ) time.sleep(1 ) print (f'{name} 的任务已经结束' ) if __name__ == '__main__' : p = Process(target=task, args=('cwz' ,)) p.start() print ('主进程' ) ''' 主进程 cwz的任务开始执行 cwz的任务已经结束 ''' class MyProcess (Process ): def run (self ): print ('开始执行任务' ) time.sleep(1 ) print ('结束执行任务' ) if __name__ == '__main__' : p = MyProcess() p.start() print ('主进程' ) ''' 主进程 开始执行任务 结束执行任务 '''
join方法的使用 用来告诉操作系统,让子进程结束后再结束父进程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from multiprocessing import Processimport timedef task (name ): print (f'{name} start...' ) time.sleep(2 ) print (f'{name} over...' ) if __name__ == '__main__' : p = Process(target=task, args=('neo' , )) p.start() p.join() print ('主进程' ) ''' neo start... neo over... 主进程 '''
进程间数据是相互隔离的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from multiprocessing import Processimport timex = 100 def func (): print ('执行func函数' ) global x x = 200 if __name__ == '__main__' : p = Process(target=func) p.start() print (x) print ('主进程' ) ''' 100 主进程 执行func函数 '''
进程间数据相互隔离: 主进程与子进程间会有各自的名称空间
进程对象的属性
p.daemon
:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()
之前设置
p.name
:进程的名称
p.pid
:进程的pid
p.exitcode
:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
p.authkey
:进程的身份验证键,默认是由os.urandom()
随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from multiprocessing import Processfrom multiprocessing import current_processimport timeimport osdef task (name ): print (f'{name} start...' , current_process().pid) time.sleep(2 ) print (f'{name} over...' , current_process().pid) if __name__ == '__main__' : p = Process(target=task, args=('neo' , )) p.start() print ('主进程' , os.getpid()) print ('主主进程' , os.getppid()) ''' 主进程 12576 主主进程 12476 neo start... 7772 neo over... 7772 '''
进程号回收的两种条件:
join, 可以回收子进程与主进程
主进程正常结束,子进程与主进程也会回收
主进程这里指的是python解释器
主主进程指的是pycharm
p.is_alive 可以查看子进程存活状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 from multiprocessing import Processfrom multiprocessing import current_processimport timeimport osdef task (name ): print (f'{name} start...' , current_process().pid) time.sleep(2 ) print (f'{name} over...' , current_process().pid) if __name__ == '__main__' : p = Process(target=task, args=('neo' , )) p.start() print (p.is_alive()) p.terminate() time.sleep(1 ) print (p.is_alive()) print ('主进程' , os.getpid()) print ('主主进程' , os.getppid()) ''' True False 主进程 10708 主主进程 12476 '''
僵尸进程和孤儿进程 僵尸进程 指的是子进程已经结束,但PID号还存在,未销毁
僵尸进程会占用PID号,占用操作系统资源
孤儿进程 指的是子进程还在执行,但父进程意外结束。
操作系统内部优化机制:会自动回收没有父的子进程
守护进程 指的是主进程结束后,该主进程产生的所有子进程跟着结束,并回收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from multiprocessing import Processfrom multiprocessing import current_processimport timedef task (name ): print (f'{name} start...' , current_process().pid) time.sleep(3 ) print (f'{name} over...' , current_process().pid) print ('子进程' ) if __name__ == '__main__' : p = Process(target=task, args=('cwz' , )) p.daemon = True p.start() print ('主进程' ) ''' 主进程 '''
TCP协议套接字,服务端实现接收客户端的连接并发 多进程实现 服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from multiprocessing import Processimport socketdef task (conn, addr ): while True : try : data = conn.recv(1024 ) if not data: break print (addr) print (data.decode('utf-8' )) conn.send(data.upper()) except Exception: break if __name__ == '__main__' : server = socket.socket() server.bind(('127.0.0.1' , 9999 )) server.listen(5 ) while True : conn, addr = server.accept() print (addr) p = Process(target=task, args=(conn, addr)) p.start()
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import socketclient = socket.socket() client.connect(('127.0.0.1' , 9999 )) while True : send_msg = input ('客户端:' ) if send_msg == 'q' : break client.send(send_msg.encode('utf-8' )) data = client.recv(1024 ) print (data.decode('utf-8' ))
多线程实现 服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import socketfrom threading import Threadserver = socket.socket() server.bind(('127.0.0.1' , 9999 )) server.listen(5 ) def task (conn ): while True : data = conn.recv(1024 ) if len (data) == 0 : continue print (data.decode('utf-8' )) conn.send(data.upper()) if __name__ == '__main__' : while True : conn, addr = server.accept() print (addr) t = Thread(target=task, args=(conn,)) t.start()
客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import socketclient = socket.socket() client.connect(('127.0.0.1' , 9999 )) while True : send_msg = input ('客户端:' ) if send_msg == 'q' : break client.send(send_msg.encode('utf-8' )) data = client.recv(1024 ) print (data.decode('utf-8' ))
进程互斥锁 多进程同时抢购余票 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import jsonimport timefrom multiprocessing import Processdef search (user ): with open ('data.json' , 'r' , encoding='utf-8' ) as f: dic = json.load(f) print (f'用户{user} 查看余票,还剩{dic.get("ticket_num" )} ...' ) def buy (user ): with open ('data.json' , 'r' , encoding='utf-8' ) as f: dic = json.load(f) time.sleep(0.1 ) if dic['ticket_num' ] > 0 : dic['ticket_num' ] -= 1 with open ('data.json' , 'w' , encoding='utf-8' ) as f: json.dump(dic, f) print (f'用户{user} 抢票成功!' ) else : print (f'用户{user} 抢票失败' ) def run (user ): search(user) buy(user) if __name__ == '__main__' : for i in range (10 ): p = Process(target=run, args=(f'用户{i} ' , )) p.start()
使用锁来保证数据安全 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import jsonimport timefrom multiprocessing import Process, Lockdef search (user ): with open ('data.json' , 'r' , encoding='utf-8' ) as f: dic = json.load(f) print (f'用户{user} 查看余票,还剩{dic.get("ticket_num" )} ...' ) def buy (user ): with open ('data.json' , 'r' , encoding='utf-8' ) as f: dic = json.load(f) time.sleep(0.2 ) if dic['ticket_num' ] > 0 : dic['ticket_num' ] -= 1 with open ('data.json' , 'w' , encoding='utf-8' ) as f: json.dump(dic, f) print (f'用户{user} 抢票成功!' ) else : print (f'用户{user} 抢票失败' ) def run (user, mutex ): search(user) mutex.acquire() buy(user) mutex.release() if __name__ == '__main__' : mutex = Lock() for i in range (10 ): p = Process(target=run, args=(f'用户{i} ' , mutex)) p.start()
进程互斥锁:
让并发变成串行,牺牲了执行效率,保证了数据安全
在程序并发时,需要修改数据使用
进程队列 队列 队列遵循的是先进先出
队列:相当于内存中一个队列空间,可以存放多个数据,但数据的顺序是由先进去的排在前面。
q.put()
添加数据
q.get()
取数据,遵循队列先进先出
q.get_nowait()
获取队列数据, 队列中没有就会报错
q.put_nowait
添加数据,若队列满了也会报错
q.full()
查看队列是否满了
q.empty()
查看队列是否为空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 from multiprocessing import Queueq = Queue(5 ) q.put(1 ) print ('进入数据1' )q.put(2 ) print ('进入数据2' )q.put(3 ) print ('进入数据3' )q.put(4 ) print ('进入数据4' )q.put(5 ) print ('进入数据5' )print (q.full())q.put_nowait(6 ) print (q.get())print (q.get())print (q.get())print (q.get())print (q.get())print (q.get_nowait()) print (q.empty())q.put(6 ) print ('进入数据6' )
进程间通信 IPC(Inter-Process Communication)
进程间数据是相互隔离的,若想实现进程间通信,可以利用队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from multiprocessing import Process, Queuedef task1 (q ): data = 'hello 你好' q.put(data) print ('进程1添加数据到队列' ) def task2 (q ): print (q.get()) print ('进程2从队列中获取数据' ) if __name__ == '__main__' : q = Queue() p1 = Process(target=task1, args=(q, )) p2 = Process(target=task2, args=(q, )) p1.start() p2.start() print ('主进程' )
生产者与消费者 在程序中,通过队列生产者把数据添加到队列中,消费者从队列中获取数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 from multiprocessing import Process, Queueimport timedef producer (name, food, q ): for i in range (10 ): data = food, i msg = f'用户{name} 开始制作{data} ' print (msg) q.put(data) time.sleep(0.1 ) def consumer (name, q ): while True : data = q.get() if not data: break print (f'用户{name} 开始吃{data} ' ) if __name__ == '__main__' : q = Queue() p1 = Process(target=producer, args=('neo' , '煎饼' , q)) p2 = Process(target=producer, args=('wick' , '肉包' , q)) c1 = Process(target=consumer, args=('cwz' , q)) c2 = Process(target=consumer, args=('woods' , q)) p1.start() p2.start() c1.daemon = True c2.daemon = True c1.start() c2.start() print ('主' )
线程 线程的概念 进程与线程都是虚拟单位
进程:资源单位
线程:执行单位
开启一个进程,一定会有一个线程,线程才是真正执行者
开启进程:
开辟一个名称空间,每开启一个进程都会占用一份内存资源
会自带一个线程
开启线程:
注意:线程不能实现并行,线程只能实现并发,进程可以实现并行
线程的两种创建方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from threading import Threadimport timedef task (): print ('线程开启' ) time.sleep(1 ) print ('线程结束' ) if __name__ == '__main__' : t = Thread(target=task) t.start() class MyThread (Thread ): def run (self ): print ('线程开启...' ) time.sleep(1 ) print ('线程结束...' ) if __name__ == '__main__' : t = MyThread() t.start()
线程对象的方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from threading import Threadfrom threading import current_threadimport timedef task (): print (f'线程开启{current_thread().name} ' ) time.sleep(1 ) print (f'线程结束{current_thread().name} ' ) if __name__ == '__main__' : t = Thread(target=task) print (t.isAlive()) t.start() print (t.isAlive())
线程互斥锁 线程之间数据是共享的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from threading import Threadfrom threading import Lockimport timemutex = Lock() n = 100 def task (i ): print (f'线程{i} 启动' ) global n mutex.acquire() temp = n time.sleep(0.1 ) n = temp - 1 print (n) mutex.release() if __name__ == '__main__' : t_l = [] for i in range (100 ): t = Thread(target=task, args=(i, )) t_l.append(t) t.start() for t in t_l: t.join() print (n)
GIL全局解释锁 Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。
全局解释锁特点:
GIL本质上是一个互斥锁。
GIL是为了阻止同一个进程内多个进程同时执行(并行)
这把锁主要是因为Cpython的内存管理不是线程安全的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from threading import Threadimport timenum = 100 def task (): global num num2 = num time.sleep(1 ) num = num2 - 1 print (num) for line in range (100 ): t = Thread(target=task) t.start()
在多线程环境中,Python 虚拟机按以下方式执行:
设置 GIL;
切换到一个线程去运行;
运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));
把线程设置为睡眠状态;
解锁 GIL;
再次重复以上所有步骤。
多线程的作用 1、计算密集型, 有四个任务,每个任务需要10s 单核:
多核:
2、IO密集型, 四个任务, 每个任务需要10s 单核:
多核:
开启进程
并行执行, 效率小于多线程, 但是遇到IO会立马切换CPU的执行权限
4个进程: 40s + 开启进程消耗的额外时间
开启线程
测试计算密集型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 from threading import Threadfrom multiprocessing import Processimport timeimport osdef work1 (): number = 0 for line in range (100000000 ): number += 1 def work2 (): time.sleep(2 ) if __name__ == '__main__' : print (os.cpu_count()) start = time.time() list1 = [] for line in range (6 ): p = Process(target=work1) list1.append(p) p.start() for p in list1: p.join() end = time.time() print (f'程序执行时间{end - start} ' )
IO密集型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 from threading import Threadfrom multiprocessing import Processimport timeimport osdef work1 (): number = 0 for line in range (100000000 ): number += 1 def work2 (): time.sleep(1 ) if __name__ == '__main__' : print (os.cpu_count()) start = time.time() list1 = [] for line in range (100 ): p = Thread(target=work2) list1.append(p) p.start() for p in list1: p.join() end = time.time() print (f'程序执行时间{end - start} ' )
结论:
在计算密集型的情况下, 使用多进程
在IO密集型的情况下, 使用多线程
高效执行多个进程, 内有多个IO密集型程序,使用多进程 + 多线程
死锁现象 指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,如无外力作用,它们都无法推进下去.此时称系统处于死锁状态
以下就是死锁:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 from threading import Thread, Lockfrom threading import current_threadimport timemutex_a = Lock() mutex_b = Lock() class MyThread (Thread ): def run (self ): self.func1() self.func2() def func1 (self ): mutex_a.acquire() print (f'用户{self.name} 抢到锁a' ) mutex_b.acquire() print (f'用户{self.name} 抢到锁b' ) mutex_b.release() print (f'用户{self.name} 释放锁b' ) mutex_a.release() print (f'用户{self.name} 释放锁a' ) def func2 (self ): mutex_b.acquire() print (f'用户{self.name} 抢到锁b' ) time.sleep(1 ) mutex_a.acquire() print (f'用户{self.name} 抢到锁a' ) mutex_a.release() print (f'用户{self.name} 释放锁a' ) mutex_b.release() print (f'用户{self.name} 释放锁b' ) for line in range (10 ): t = MyThread() t.start() ''' 用户Thread-1抢到锁a 用户Thread-1抢到锁b 用户Thread-1释放锁b 用户Thread-1释放锁a 用户Thread-1抢到锁b 用户Thread-2抢到锁a '''
递归锁 用于解决死锁问题
RLock: 比喻成万能钥匙,可以提供给多个人使用
但是第一个使用的时候,会对该锁做一个引用计数
只有引用计数为0, 才能真正释放让一个人使用
上面的例子中用RLock代替Lock, 就不会发生死锁现象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 from threading import Thread, Lock, RLockfrom threading import current_threadimport timemutex_a = mutex_b = RLock() class MyThread (Thread ): def run (self ): self.func1() self.func2() def func1 (self ): mutex_a.acquire() print (f'用户{self.name} 抢到锁a' ) mutex_b.acquire() print (f'用户{self.name} 抢到锁b' ) mutex_b.release() print (f'用户{self.name} 释放锁b' ) mutex_a.release() print (f'用户{self.name} 释放锁a' ) def func2 (self ): mutex_b.acquire() print (f'用户{self.name} 抢到锁b' ) time.sleep(1 ) mutex_a.acquire() print (f'用户{self.name} 抢到锁a' ) mutex_a.release() print (f'用户{self.name} 释放锁a' ) mutex_b.release() print (f'用户{self.name} 释放锁b' ) for line in range (10 ): t = MyThread() t.start()
信号量 互斥锁: 比喻成一个家用马桶, 同一时间只能让一个人去使用
信号比喻成公测多个马桶: 同一时间可以让多个人去使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from threading import Semaphorefrom threading import Threadfrom threading import current_threadimport timesm = Semaphore(5 ) def task (): sm.acquire() print (f'{current_thread().name} 执行任务' ) time.sleep(1 ) sm.release() for i in range (20 ): t = Thread(target=task) t.start()
线程队列 线程Q: 就是线程队列 FIFO
普通队列: 先进先出 FIFO
特殊队列: 后进先出 LIFO
优先级队列: 若传入一个元组,会依次判断参数的ASCII的数值大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import queueq = queue.Queue() q.put(1 ) q.put(2 ) q.put(3 ) print (q.get()) print (q.get()) q = queue.LifoQueue() q.put(1 ) q.put(2 ) q.put(3 ) print (q.get()) q = queue.PriorityQueue() q.put((4 , '我' )) q.put((2 , '你' )) q.put((3 , 'ta' )) print (q.get())
Event事件 用来控制线程的执行
出现e.wait()
,就会把这个线程设置为False,就不能执行这个任务;
只要有一个线程出现e.set()
,就会告诉Event对象,把有e.wait
的用户全部改为True,剩余的任务就会立马去执行。由一些线程去控制另一些线程,中间通过Event。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 from threading import Eventfrom threading import Threadimport timee = Event() def light (): print ('红灯亮...' ) time.sleep(5 ) e.set () print ('绿灯亮...' ) def car (name ): print ('正在等红灯...' ) e.wait() print (f'{name} 正在加速飘逸...' ) t = Thread(target=light) t.start() for i in range (10 ): t = Thread(target=car, args=(f'汽车{i} 号' , )) t.start()
进程池与线程池
进程池与线程池是用来控制当前程序允许创建(进程/线程)的数量
作用:保证在硬件允许的范围内创建(进程/线程)的数量
线程池使用一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from concurrent.futures import ThreadPoolExecutorimport timepool = ThreadPoolExecutor(5 ) def task (): print ('线程任务开始了...' ) time.sleep(1 ) print ('线程任务结束了...' ) for line in range (5 ): pool.submit(task)
使用二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from concurrent.futures import ThreadPoolExecutorimport timepool = ThreadPoolExecutor(5 ) def task (): print ('线程任务开始了...' ) time.sleep(1 ) print ('线程任务结束了...' ) return 123 def call_back (res ): print (type (res)) res2 = res.result() print (res2) for line in range (5 ): pool.submit(task).add_done_callback(call_back)
pool.shutdown()
会让所有线程池的任务结束后,才往下执行代码
协程
进程: 资源单位
线程: 执行单位
协程: 在单线程下实现并发
注意: 协程不是操作系统资源,目的是让单线程实现并发
协程目的
操作系统:使用多道技术,切换 + 保存状态,一个是遇到IO, 另一个是CPU执行时间过长
协程:通过手动模拟操作系统 “多道计数”, 实现 切换 + 保存状态
手动实现,遇到IO切换,欺骗操作系统误以为没有IO操作
单线程时,遇到IO,就切换 + 保存状态
单线程时,对于计算密集型,来回切换 + 保存状态反而效率更低
优点:在IO密集型的情况下,会提高效率
缺点:若在计算密集型的情况下,来回切换,反而效率更低
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import timedef func1 (): for i in range (10000000 ): i+1 def func2 (): for i in range (10000000 ): i+1 start = time.time() func1() func2() stop = time.time() print (stop - start) def func1 (): while True : 10000000 +1 yield def func2 (): g = func1() for i in range (10000000 ): i+1 next (g) start = time.time() func2() stop = time.time() print (stop - start)
gevent gevent模块介绍 gevent是一个第三方模块,可以帮你监听IO操作,并切换
使用gevent的目的:在单线程下实现,遇到IO就会 保存状态 + 切换
gevent使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import timefrom gevent import monkeymonkey.patch_all() from gevent import spawn, joinall def func1 (): print ('1' ) time.sleep(1 ) def func2 (): print ('2' ) time.sleep(3 ) def func3 (): print ('3' ) time.sleep(5 ) start = time.time() s1 = spawn(func1) s2 = spawn(func2) s3 = spawn(func3) s1.join() s2.join() s3.join() end = time.time() print (end - start)
TCP服务端socket套接字实现协程 服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 from gevent import monkeyfrom gevent import spawnimport socketmonkey.patch_all() server = socket.socket() server.bind(('127.0.0.1' , 9999 )) server.listen(5 ) def task (conn ): while True : try : data = conn.recv(1024 ) if len (data) == 0 : break print (data.decode('utf-8' )) send_data = data.upper() conn.send(send_data) except Exception: break conn.close() def server2 (): while True : conn, addr = server.accept() print (addr) spawn(task, conn) if __name__ == '__main__' : s = spawn(server2) s.join()
客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import socketfrom threading import Thread, current_threaddef client (): client = socket.socket() client.connect(('127.0.0.1' , 9999 )) number = 0 while True : send_data = f'{current_thread().name} {number} ' client.send(send_data.encode('utf-8' )) data = client.recv(1024 ) print (data.decode('utf-8' )) number += 1 for i in range (400 ): t = Thread(target=client) t.start()