day 34
1 .内容回顾
#__author : 'liuyang' #date : 2019/4/17 0017 上午 9:01 # 利大于弊,则做之 # 会死于斯,则不去 # 2个 人 晚上 5个题 面试题 # 今晚 7点 考试题 # 五一之前 要考试 #其它内容(40%) 网编并发数据库(60%) #锁 #互斥锁 #能够保护数据的安全性 #保证对于数据的修改操作同一时刻多个进程只有一个进程执行 #进程数据不安全:同时修改文件/数据库/其它共享资源的数据 # 队列---实现了进程之间的通信(IPC) #进程队列 ----进程安全 #从mutltiprocessing 导入Queue #q = Queue() #put/get # 基于管道+管道 ,管道 进程不安全 # 管道 基于文件级别的socket 实现的 import queue from multiprocessing import Queue # 生产者消费模型 # put get 两个阻塞方法 # put_nowait(丢数据)/ get_nowait 非阻塞方法 q = Queue(5) #队列里有五个 q.put(1) q.put(1) q.put(1) q.put(1) q.put(1) print('_____________') try : q.put_nowait(1) #也传一个 except queue.Full: # 存在这里 pass # q.put(1) print('______') # q.put(1) print('__') print(q.get()) print(q.get()) print(q.get()) print(q.get()) print(q.get()) print(q.get()) # print(q.get()) # print(q.get()) # print(q.get()) # print(q.get()) try : print(q.get_nowait()) except queue.Empty: pass # 网页版的 qq # sereve 端 djiong 写的 # 信息一直没有 一直夯在这 于是 用get_nowait() 没数据立刻返回 # 隔个0.5秒 再返回来查看 # 生产者 消费者 模型 # 达到效率平衡 # 一个生产数据和消费数据的完整的流程解耦解耦成两个部分:生产和消费 #由于生产速度和消费速度不一致,所以需要我们调整生产者和消费者额d个数来达到效率平衡
2.今日内容
#1 互斥锁
#2 进程之间的数据共享
#关于数据安全的问题
#3 进程池
#from multiprocessing import Pool
# 事件\信号量\管道
# 4 线程的概念(面试的重点)
# 5 认识线程模块
3.什么是互斥锁
# 在多个进程中 或者一个进程中
# 有acquire()acquire()就阻在那里了
#__author : 'liuyang' #date : 2019/4/17 0017 上午 9:27 # from multiprocessing import Lock # #1 互斥锁:不能在同一个进程内,‘也’有锁的竞争关系 # # 在同一个进程中连续acquire多次会产生死锁 # lock = Lock() # lock.acquire() #拿走钥匙 # print(1) # lock.acquire() #被拿走了又想要钥匙 卡住 #互斥 # print(2) # # lock.release() #没还钥匙 卡在第一个开锁那 # lock.release() # lock.release()
4.数据共享(进程内可以但是不大需要)
# 本来是数据隔离的
# 加上Manager就可以分享了
#__author : 'liuyang' #date : 2019/4/17 0017 上午 9:35 #数据共享几乎不用 # 数据共享: 可以, Manager 但是不安全 # 可以 安全 加锁 from multiprocessing import Process,Manager,Lock def func(dic): dic['count'] -= 1 # 数据不共享 正常情况下数据隔离 子进程改 if __name__ == '__main__': lock = Lock() m = Manager() dic = m.dict({'count':100}) p_l = [] for i in range(100): p = Process(target=func,args=(dic,lock)) p.start() p_l.append(p) for p in p_l : p.join() print(dic) #{'count': 0} 性能越好 越不得零 # 100 减 100次 1 这么慢? 不是减操作造成的 # 慢是因为 开100个进程 所以慢 开和管理 销毁进程拖慢了程序的运行效率 # 为什么在这里出现了数据不安全的现象? # 正常不会数据共享 隔离 肯定通过socket 文本或者网络传过来的 # (不丢失)性能快的时候,比如两个 子进程同时对一样的值操作了,返回的一样的值 #什么情况会出现数据不安全:Manager 类当中对字典/列表 += -= *= /= #添加值 不会 #如何解决 : 加锁 # 修改的 异步 性能高了会出错对同一个进行操作 #加锁同步安全 # 查看的 异步快 # cpu个数 最多起cpu*2个 #
5.多线程
cpython解释器内置的 GIL锁,同步
为了数据安全
解释性语言(一边解释一边编译 解释的字节码 无序 所以操作造成异步操作 数据不安全)
编译型语言可以实现多进程
#__author : 'liuyang' #date : 2019/4/17 0017 上午 10:18 # 线程是进程中的一个单位 # 进程是计算机中最小的资源分配单位 # 线程是计算机中被CPU调度的最小单位 # 开启进程 关闭进程 切换进程都需要时间 # 你的电脑的资源还是优先的 # 开启过多的进程会导致你的计算机崩溃 # count_cpu*2 = 8 #4 核 # 同时对8个客户端服务 # qps 每秒的请求数 2W # 2000/8 = 250 台机器 # 进程: 数据隔离的 #ftp server端 #qq server端(不用隔离,本来就不传输,得写了才传) # 并发: 同一时刻能同时接受多个客户端的请求 # 进程有大 有数据隔离 开多了还不行 (有的不需要隔离,还要ipc传输过来、) #线程: # 轻型进程 轻量级的进程 # 在同一个进程中的多个线程是可以共享一部分数据的 # 线程的开启\销毁\切换都比进程要高级很多 #1.多个进程可不可以利用多核(多个CPU) #2.多个线程可不可以利用多核(多个CPU) # 都可以 # 多进程和多线程之间的区别 #进程 数据隔离 开销大 (必须数据隔离是才会进程) #线程 数据共享 开销小 # python 当中的多线程 # Cpython 解释器 有一个GIL锁 (怕麻烦 要不太多修改的) # # (解释性语言) 编译 --》字节码(bytes) 没法正常排序的-->机器码(0101) # python 代码机器语言 from dis import dis def func(): a = [] a.append(1) dis(func) ''' 42 0 BUILD_LIST 0 2 STORE_FAST 0 (a) 43 4 LOAD_FAST 0 (a) 6 LOAD_ATTR 0 (append) 8 LOAD_CONST 1 (1) #加载他们几个存在内存里 10 CALL_FUNCTION 1 #调用 12 POP_TOP 14 LOAD_CONST 0 (None) 16 RETURN_VALUE''' #默认返回空 # 两个程序在两个cpu上同时触发,就可能造成数据不安全 # python 解释器(自动加锁) 同一时刻 只能有一个线程执行(单线程) # 解释性语言(python,php) 都不能多个程序(线程)访问多个cpu #(异步执行无序)所以数据不安全 # 编译型语言(c,java) 都能多个程序(线程)访问多个cpu # GIL 锁 历史遗留问题(python诞生时没有多核) #线程中的状态 就不如编译型语言 那么完美 # python 当中的多线程(不是python的锅,可以解决的,没必要) # 不能访问多个cpu # 是Cpython(解决不了高运算) 解释器导致的 #不同的工具不同的功能 #每个工具都不是万能的 # GIL = 全局解释器锁 导致了同一时刻只能有一个线程访问cpu # pypy 没有GIL锁 Jpython 解释成java 可以多线程访问多核的 (但是他们的执行效率慢) # 研究pypy(和cpython速度相近了) 不如直接研究 go # go 的执行效率 和 c相近了 #利用 多核意味着 多个CPU可以同时计算线程中的代码 # IO 操作 #accept负责从网络上读取数据 #open() 调用操作系统的指令 0.9ms的时间 (cpu执行450万条指令(在阻塞)) #read #7个指令 一行代码 50w/7 7w条 python 代码 千分之一秒 执行的 # 分布式\多进程 #大量的计算 可以开 多进程 进多核 高额业务 # ftp 这样的大部分都是网络和进程都无关 # web 框架 #大部分时间 都是金阻塞 io呢 文件呢 网络呢 没大进cpu进行 #所以单进程多线程就够了 #Django flask tornado twistwed scrapy sanic #都 并发 多线程 没有用进程 # 解释性和 编译型 的 因为一边翻译 一边编译 不如 直接汉语好 英语放过来
6.python代码实现 thread(进程)
#__author : 'liuyang' #date : 2019/4/17 0017 上午 11:34 # 利 = 1 # 弊 = 2 # do = 'do' # if 利 > 弊: # do # import threading # threading 和 multtiprocessing # 先有的threading模块 #没有池的功能 #multiprocessing 完全模仿threading 模块完成的 #实现了池的功能 #concurrent.futures #实现了线程池\进程池 # import os # import time # from threading import Thread # def func(i): #子线程 # time.sleep(1) #阻塞了之后 cpu就绪 执行就不在控制范围内 了所以不按顺序 # print('in func',i, os.getpid()) # # print('in main',os.getpid()) # #一个线程的时间开启和管理的时间不是固定的 开的时候有可能cpu很闲 # #直接执行了 # # for i in range(20): #并发 #异步不是一起执行 # Thread(target=func,args=(i,)).start() #func() #同步20 秒 20倍差别 #in main 4676 # in func 4676 # 在一个进程里 # if __name__ =='__main__': #多个线程都是 共享的 没有import没有导入 # 不会被执行 所以 不用 # 在线程部分不需要通过import来为新的线程获取代码 # 因为新的线程和之前的主线程共享同一段代码 # 不需要import 也就不存在 子进程中有重复了一次 # 开销 # 进程和线程的代码管理 是一样的 因为都是开和关 没必要有差距 # 数据隔离 # # from multiprocessing import Process # from threading import Thread # import time # def func(a): # a += 1 # if __name__ =='__main__': # start = time.time() # t_l = [] # # for i in range(100): # t = Thread(target=func,args=(i,)) # t.start() # t_l.append(t) # for t in t_l : t.join() # 确保前面都执行完 # print('thread',time.time() - start ) # # start = time.time() # t_l = [] # # for i in range(100): # t = Process(target=func, args=(i,)) # t.start() # t_l.append(t) # for t in t_l: t.join() # 确保前面都执行完 # print('process',time.time() - start) # # #t 0.0870048999786377 # # p 7.8074469566345215 # 100倍 开进程倍 # 多个线程之间的全局变量是共享的 # # from multiprocessing import Process # from threading import Thread # import time # tn = 0 # def func(a): # global tn # tn += 1 # if __name__ =='__main__': # start = time.time() # t_l = [] # # for i in range(100): # t = Thread(target=func,args=(i,)) # t.start() # t_l.append(t) # for t in t_l : t.join() # 确保前面都执行完 # print('thread',time.time() - start ) # print(tn) # # # 进程之间数据隔离 # pn = 0 # 这里定义了 所以不报错 # def func(a): # global pn # pn += 1 # # if __name__ == '__main__': # # start = time.time() # t_l = [] # # for i in range(100): # t = Process(target=func, args=(i,)) # t.start() # t_l.append(t) # for t in t_l: t.join() # 确保前面都执行完 # print('process',time.time() - start) # print(pn) # 线程中的几个替他方法 from threading import Thread,currentThread #线程对象 import os def func(): t = currentThread() #线程属于哪个id的 print(t.name,t.ident,os.getpid()) # print(currentThread(),os.getpid()) tobj = Thread(target=func) tobj.start() print('tobj:',tobj) # print(currentThread(),os.getpid()) ''' Thread-1 9224 9612 <_MainThread(MainThread, started 4108)> 9612 <Thread(Thread-1, started 9224)> 9612 ''' ''' Thread-1 8800 9588 #巧了 异步一样 tobj: <Thread(Thread-1, stopped 8800)>''' lst = [1,2,3,4,5,6,7,8,9,10] #按照顺序把列表中的每一个元素都计算一个平方 #使用多线程方式 # #并且将结果按照顺序返回 from threading import Thread #线程对象 import time import random dic = {} def func(i): t = currentThread() time.sleep(random.random()) dic[t.ident] = i**2 t_l = [] for i in range(1,11): t = Thread(target=func, args=(i,)) t.start() t_l.append(t) for t in t_l: t.join() # 确保前面都执行完 print(dic[t.ident]) print(dic) #{7936: 81, 6056: 4, 9344: 49, 10188: 9, 6648: 100, 7264: 64, 4072: 16, 9924: 36, 8796: 1, 7216: 25} #这不是 按顺序添加的 只是显示是顺序的 添加是按顺序的 # from threading import active_count # # 返回当前正在工作的线程 # from threading import Thread #线程对象 # import time # import random # l = [] # def func(a): # t = currentThread() # time.sleep(random.random()) # for i in range(1,11): # t = Thread(target=func, args=(i,)) # #t.start() 11 个 不start() 就不执行 # print(active_count()) #1 #只有一个主线程 # 线程有terminate 么? #没有terminate 不能强制结束 #所有的子线程都会在执行完所有的任务之后结束 # 统计线程的id 统计正在执行的线程数 用法差不多 没有terminate # 1 . 通读博客 :操作系统 ,进程,线程 # 2 . 把课上的代码都搞明白 # 3. 多线程实现一个并发 tcp协议的socket_server # 4. 明天默写和线程相关的