网络并发
网络并发
一 操作系统的历史与初步认识
1.人机矛盾:
CPU利用率低
磁带存储 + 批处理
降低数据的读取时间
提高cpu的利用率
2.多道操作系统—— 在一个任务遇到IO的时候主动让出CPU
数据隔离
时空复用
能够在一个任务遇到io操作的时候主动把cpu让出来,给其他的任务使用
3.分时操作系统 —— 给时间分片,让多个任务轮流使用CPU
4.分布式操作系统
5.网络操作系统
二.进程
进程初识:
进程也就是说是运行中的程序 :是计算机最小额资源分配单位:进程的调度室友操作系统完成的;
三状态:
就绪 -system call-><-时间片到了- 运行 -io-> 阻塞-IO结束->就绪
阻塞 影响了程序运行的效率
同步与异步:
同步:调用一个方法要等待这个方法结束
异步:调用一个方法 不等待这个方法结束 也不关心这个方法做了什么
阻塞与非阻塞:
阻塞:cpu不工作
非阻塞:cpu工作
并发与并行:
并发:并发是指多个程序 公用一个cpu轮流使用。
并行:多个程序 多个cpu 一个cpu上运行一个程序,
在一个时间点上看,多个程序同时在多个cpu上运行。
进程的开启与结束:
1.开启一个进程:
函数名(参数1,参数2) from multiprocessing import Process p = Process(target=函数名,args=(参数1,参数2)) p.start()
2.父进程和子进程
3.父进程会等待着所有的子进程结束之后才结束:为了回收资源
4.:进程开启的过程中windows和 linux/ios之间的区别
开启进程的过程需要放在if __name__ == '__main__'下windows中 相当于在子进程中把主进程文件又从头到尾执行了一遍,
除了放在if __name__ == '__main__'下的代码,linux中 不执行代码,直接执行调用的func函数
5.join的方法:
把一个进程的结束事件封装成一个join方法
执行join方法的效果就是 阻塞直到这个子进程执行结束就结束阻塞
在多个子进程中使用join p_l= [] for i in range(10): p = Process(target=函数名,args=(参数1,参数2)) p.start() p_l.append(p) for p in p_l:p.join() 所有的子进程都结束之后要执行的代码写在这里
6.mulitiprocessing进程
p=Process(target=函数名。arge = (参数1,)
7.实例
import demo from multiprocessing import Process def target(i): print(i) if __name__ == '__main__': p_l = [] for i in range(5): p = Process(target=target,args=(i,)) p.start() p_l.append(p) # p.join() # 阻塞 主进程 直到子进程执行完毕 for p in p_l:p.join() print('子进程已经都执行完毕了') mp2 = MyProcecss2() mp2.start() print('main :',os.getpid()) time.sleep(1)
8..守护进程
1.例子1
import time from multiprocessing import Process def son1(a,b): while True: print('is alive') time.sleep(0.5) def son2(): for i in range(5): print('in son2') time.sleep(1) if __name__ == '__main__': p = Process(target=son1,args=(1,2)) p.daemon = True p.start() # 把p子进程设置成了一个守护进程 p2 = Process(target=son2) p2.start() time.sleep(2)
2.面向对象
# 有一个参数可以把一个子进程设置为一个守护进程 import time from multiprocessing import Process def son1(): while True: print('is alive') time.sleep(0.5) if __name__ == '__main__': p = Process(target=son1) p.start() # 异步 非阻塞 print(p.is_alive()) time.sleep(1) p.terminate() # 异步的 非阻塞 print(p.is_alive()) # 进程还活着 因为操作系统还没来得及关闭进程 time.sleep(0.01) print(p.is_alive()) # 操作系统已经响应了我们要关闭进程的需求,再去检测的时候,得到的结果是进程已经结束了
9.进程之间的通信
进程之间的通信 - IPC(inter process communication)
# from multiprocessing import Queue,Process # # 先进先出 def func(exp,q): ret = eval(exp) q.put({ret,2,3}) q.put(ret*2) q.put(ret*4) if __name__ == '__main__': q = Queue() Process(target=func,args=('1+2+3',q)).start() print(q.get()) print(q.get()) print(q.get())
三.线程
1.线程的优缺点:
线程是进程的一部分,每个进程中至少有一个线程
能被CPU调度的最小单位
一个进程中的多个线程是可以共享这个进程的数据的
线程的创建,销毁,切换,开销远远小于进程---开销小
不能利用多核
2.threading模块:
线程中的几个方法:
属于线程对象t.start(),t.join()
守护线程t.daemon = True 等待所有的非守护子线程都结束之后才结束
非守护线程不结束,主线程也不结束
主线程结束了,主进程也结束
结束顺序 :非守护线程结束 -->主线程结束-->主进程结束
-->主进程结束 --> 守护线程也结束
3.线程之间的通信——队列
from queue import Queue # 先进先出队列 # q = Queue(5) # q.put(0) # q.put(1) # q.put(2) # q.put(3) # q.put(4) # print('444444') # # # print(q.get()) # print(q.get()) # print(q.get()) # print(q.get()) # print(q.get())
后进先出
from queue import LifoQueue # 后进先出队列 # last in first out 栈 # lfq = LifoQueue(4) # lfq.put(1) # lfq.put(3) # lfq.put(2) # print(lfq.get()) # print(lfq.get()) # print(lfq.get())
优先级
# from queue import PriorityQueue # pq = PriorityQueue() # pq.put((10,'alex')) # pq.put((6,'wusir')) # pq.put((20,'yuan')) # print(pq.get()) # print(pq.get()) # print(pq.get())
四.协程
# 线程是由 操作系统 调度,由操作系统负责切换的 # 协程: # 用户级别的,由我们自己写的python代码来控制切换的 # 是操作系统不可见的 # 在Cpython解释器下 - 协程和线程都不能利用多核,都是在一个CPU上轮流执行 # 由于多线程本身就不能利用多核 # 所以即便是开启了多个线程也只能轮流在一个CPU上执行 # 协程如果把所有任务的IO操作都规避掉,只剩下需要使用CPU的操作 # 就意味着协程就可以做到题高CPU利用率的效果 # 多线程和协程 # 线程 切换需要操作系统,开销大,操作系统不可控,给操作系统的压力大 # 操作系统对IO操作的感知更加灵敏 # 协程 切换需要python代码,开销小,用户操作可控,完全不会增加操作系统的压力 # 用户级别能够对IO操作的感知比较低
协程切换问题
# 协程 :能够在一个线程下的多个任务之间来回切换,那么每一个任务都是一个协程 # 两种切换方式 # 原生python完成 yield asyncio # C语言完成的python模块 greenlet gevent # greenlet # import time # from greenlet import greenlet # # def eat(): # print('wusir is eating') # time.sleep(0.5) # g2.switch() # print('wusir finished eat') # # def sleep(): # print('小马哥 is sleeping') # time.sleep(0.5) # print('小马哥 finished sleep') # g1.switch() # # g1 = greenlet(eat) # g2 = greenlet(sleep) # g1.switch()
gevent模块
# import time # print('-->',time.sleep) # import gevent # from gevent import monkey # monkey.patch_all() # def eat(): # print('wusir is eating') # print('in eat: ',time.sleep) # time.sleep(1) # print('wusir finished eat') # # def sleep(): # print('小马哥 is sleeping') # time.sleep(1) # print('小马哥 finished sleep') # # g1 = gevent.spawn(eat) # 创造一个协程任务 # g2 = gevent.spawn(sleep) # 创造一个协程任务 # g1.join() # 阻塞 直到g1任务完成为止 # g2.join() # 阻塞 直到g1任务完成为止
五.锁
1.实例1
import time import json from multiprocessing import Process,Lock def search_ticket(user): with open('ticket_count') as f: dic = json.load(f) print('%s查询结果 : %s张余票'%(user,dic['count'])) def buy_ticket(user,lock): # with lock: # lock.acquire() # 给这段代码加上一把锁 time.sleep(0.02) with open('ticket_count') as f: dic = json.load(f) if dic['count'] > 0: print('%s买到票了'%(user)) dic['count'] -= 1 else: print('%s没买到票' % (user)) time.sleep(0.02) with open('ticket_count','w') as f: json.dump(dic,f) # lock.release() # 给这段代码解锁 def task(user, lock): search_ticket(user) with lock: buy_ticket(user, lock) if __name__ == '__main__': lock = Lock() for i in range(10): p = Process(target=task,args=('user%s'%i,lock)) p.start()
2.互斥锁
# 互斥锁是锁中的一种:在同一个线程中,不能连续acquire多次 # from threading import Lock # lock = Lock() # lock.acquire() # print('*'*20) # lock.release() # lock.acquire() # print('-'*20) # lock.release()
3.递归锁
好 :在同一个进程中多次acquire也不会发生阻塞
不好 :占用了更多资源
递归锁解决死锁的问题
import time from threading import RLock,Thread noodle_lock = fork_lock = RLock() print(noodle_lock,fork_lock) def eat1(name,noodle_lock,fork_lock): noodle_lock.acquire() print('%s抢到面了'%name) fork_lock.acquire() print('%s抢到叉子了' % name) print('%s吃了一口面'%name) time.sleep(0.1) fork_lock.release() print('%s放下叉子了' % name) noodle_lock.release() print('%s放下面了' % name) def eat2(name,noodle_lock,fork_lock): fork_lock.acquire() print('%s抢到叉子了' % name) noodle_lock.acquire() print('%s抢到面了'%name) print('%s吃了一口面'%name) time.sleep(0.1) noodle_lock.release() print('%s放下面了' % name) fork_lock.release() print('%s放下叉子了' % name) lst = ['alex','wusir','taibai','yuan'] Thread(target=eat1,args=(lst[0],noodle_lock,fork_lock)).start() Thread(target=eat2,args=(lst[1],noodle_lock,fork_lock)).start() Thread(target=eat1,args=(lst[2],noodle_lock,fork_lock)).start() Thread(target=eat2,args=(lst[3],noodle_lock,fork_lock)).start()
六.池
# 池 # 进程池 # 线程池 # 池 # 预先的开启固定个数的进程数,当任务来临的时候,直接提交给已经开好的进程 # 让这个进程去执行就可以了 # 节省了进程,线程的开启 关闭 切换都需要时间 # 并且减轻了操作系统调度的负担
concurrent.futures 进程池
concurrent.futures import os import time import random from concurrent.futures import ProcessPoolExecutor # submit + shutdown # def func(): # print('start',os.getpid()) # time.sleep(random.randint(1,3)) # print('end', os.getpid()) # if __name__ == '__main__': # p = ProcessPoolExecutor(5) # for i in range(10): # p.submit(func) # p.shutdown() # 关闭池之后就不能继续提交任务,并且会阻塞,直到已经提交的任务完成 # print('main',os.getpid())
带参数和返回值的进程池
# def func(i,name): # print('start',os.getpid()) # time.sleep(random.randint(1,3)) # print('end', os.getpid()) # return '%s * %s'%(i,os.getpid()) # if __name__ == '__main__': # p = ProcessPoolExecutor(5) # ret_l = [] # for i in range(10): # ret = p.submit(func,i,'alex') # ret_l.append(ret) # for ret in ret_l: # print('ret-->',ret.result()) # ret.result() 同步阻塞 # print('main',os.getpid())
缺点:开销大,一个池中的任务个数限制了我们程序的并发个数
线程池
# from concurrent.futures import ThreadPoolExecutor # def func(i): # print('start', os.getpid()) # time.sleep(random.randint(1,3)) # print('end', os.getpid()) # return '%s * %s'%(i,os.getpid()) # tp = ThreadPoolExecutor(20) # ret_l = [] # for i in range(10): # ret = tp.submit(func,i) # ret_l.append(ret) # tp.shutdown() # print('main') # for ret in ret_l: # print('------>',ret.result())
回调函数
import requests from concurrent.futures import ThreadPoolExecutor def get_page(url): res = requests.get(url) return {'url':url,'content':res.text} def parserpage(ret): dic = ret.result() print(dic['url']) tp = ThreadPoolExecutor(5) url_lst = [ 'http://www.baidu.com', # 3 'http://www.cnblogs.com', # 1 'http://www.douban.com', # 1 'http://www.tencent.com', ] ret_l = [] for url in url_lst: ret = tp.submit(get_page,url) ret_l.append(ret) ret.add_done_callback(parserpage)