python之路多进程和多线程总结(四)
2018-9-23 15:57:51
终于把多进程和多线程东西整理完了!
下次整理那个数据库->前端->Django
慢慢来! 发现学了好多东西!
1. 僵尸进程,孤儿进程
1.1 僵尸进程:子进程结束,父类未结束
1.2 孤儿进程:父类进程over.,子进程未结束
''' 多线程的学习 ''' #使用线程的第一种方式,直接调用threading里面的Thread类 from threading import Thread import time def test(): print("--====昨晚喝多了,下次少喝点!!!") time.sleep(1) def main(): '''创建线程''' for i in range(5): #创建一个线程 t = Thread(target=test) t.start() if __name__ == '__main__': main() #使用线程的第二种方式,封装成一个类, import threading import time class MyThread1(threading.Thread): """创建一个类继承多线程类""" def run(self): for i in range(3): time.sleep(1) msg = "I'm" +slef.name+'@'+str(1) #name属性中保存的是当前线程的名字 print(msg) if __name__ == '__main__': t = MyThread1() t.start()
2. 线程和进程区别
进程之间不能共享全局变量
线程之间共享全局变量,线程函数中的局部变量不是共享的!!
from threading import Thread import time g_num = 100 def work1(): global g_num for i in range(3): g_num += 1 print("-----in work1,g_num is %d-----"%g_num) def work2(): global g_num print("-------in work2 ,g_num is %d-----"%g_num) print("---------线程创建之前g_num is %d---"%g_num) t1 = Thread(target=work1) t1.start() #延时一会,保证t1线程中的事情做完 time.sleep(1) t2 = Thread(target=work2) t2.start()
3. 互斥锁
关于互斥锁,解决线程占用的问题!!!
上锁的代码越少越好!该加的时候才加!
#--*utf- 8*-- from threading import Thread ,Lock import time g_num = 0 def work1(): global g_num #上锁, 这个线程和work2线程都在抢着 对这个锁进行上锁,如果由1方成功的上锁 #那么导致一方会堵塞(一直等待)到这个锁被解开位置 mutex.acquire() for i in range(100000): g_num += 1 #解锁 #用来对mutex指向的这个锁, 进行解锁,,只要开了锁,那么接下来会让所有 #因为这个锁 被锁上的锁 而堵塞的线程,进行抢着上锁 mutex.release() print("-----in work1,g_num is %d-----"%g_num) def work2(): global g_num #上锁 mutex.acquire() for i in range(100000): g_num += 1 #解锁 mutex.release() print("-------in work2 ,g_num is %d-----"%g_num) #创建一把互斥锁,这个锁默认是没有上锁的 mutex =Lock() def main(): t1 = Thread(target=work1) t1.start() #延时一会,保证t1线程中的事情做完 # time.sleep(1) #取消屏蔽之后,再次运行程序 t2 = Thread(target=work2) t2.start() if __name__ == '__main__': main()
4. 同步应用:
同步的应用!就是一个接着一个
#同步的应用! from threading import Thread,Lock from time import sleep class Task1(Thread): def run(self): while True : if lock1.acquire(): print("------Task1----------") sleep(0.5) #给Task2开锁! lock2.release() class Task2(Thread): def run(self): while True: if lock2.acquire(): print("------Task2----------") sleep(0.5) #给Task3开锁 lock3.release() class Task3(Thread): def run(self): while True: if lock3.acquire(): print("------Task3----------") sleep(0.5) lock1.release() #使用Lock创建出的锁默认没有"锁上" lock1 = Lock() #创建另外一个锁,并且锁上 lock2 = Lock() lock2.acquire() #创建另外一个锁,并且"锁上" lock3 = Lock() lock3.acquire() t1 = Task1() t2 = Task2() t3 = Task3() t1.start() t2.start() t3.start()
5.降低代码的耦合性
#解决耦合的问题 #用队列解决这种问题,起到了缓冲的作用 import threading import time # #python2中 # from Queue import Queue #python3中 from queue import Queue class Producer(threading.Thread): def run(self): global queue count = 0 while True: if queue.qsize() < 1000: for i in range(100): count = count +1 msg = '生成产品'+str(count) queue.put(msg) print(msg) time.sleep(0.5) class Consumer(threading.Thread): def run(self): global queue while True: if queue.qsize() > 100: for i in range(3): msg = self.name + '消费了 '+queue.get() print(msg) time.sleep(1) if __name__ == '__main__': queue = Queue() for i in range(500): queue.put('初始产品'+str(i)) for i in range(2): p = Producer() p.start() for i in range(5): c = Consumer() c.start()
反思: 多看别人的代码,多问自己是否能写出来这样优雅代码! 代码一定要低耦合,高优雅!!!敲代码要多思考!
6.python中的GIL,线程锁
在python中,多进程效率远大于多线程效率
python中存在GIL这个"线程锁",
关键地方可以使用c语言解决 GIL问题 然后可以提高cpu占用效率
7.异步的实现!
同步调用就是你 喊 你朋友吃饭 ,你朋友在忙 ,你就一直在那等,等你朋友忙完了 ,你们一起去
异步调用就是你 喊 你朋友吃饭 ,你朋友说知道了 ,待会忙完去找你 ,你就去做别的了。
from multiprocessing import Pool import time import os def test(): print("---进程池中的进程---pid=%d,ppid=%d--"%(os.getpid(),os.getppid())) for i in range(3): print("----%d---"%i) time.sleep(1) return "hahah" def test2(args): print("---callback func--pid=%d"%os.getpid()) print("---callback func--args=%s"%args) pool = Pool(3) pool.apply_async(func=test,callback=test2) time.sleep(5) print("----主进程-pid=%d----"%os.getpid())
8.反射
# 通过反射 # 对象名 获取对象属性 和普通方法 # 类名 获取静态属性 和类方法 和静态方法 # 普通方法 self # 静态方法 @staticmethod # 类方法 @classmethod # 属性方法@ property # 继承 # 封装 # 反射,, 应用:从类里面获取字符串 # 在python中万物皆对象 class Teacher: dic = {"查看学生信息":"show_student","查看讲师信息":"show_teacher"} def show_student(): print("show_student") def show_teacher(): print("show_teacher") if hasattr(Teacher,"dic"): #如果类中有dic 则直接调用 ret = getattr(Teacher,"dic") #Teacher.dic #类也是对象 # print(ret) alex = Teacher() for k in Teacher.dic: print(k) key = input("输入需求:") func = getattr(alex,Teacher.dic[key]) func()
9.一个_rcv面试题
#类调用属性,属性没有,用__getatrr__魔法方法! #目的打印出 think different itcast class Foo(object): def __init__(self): pass def __getattr__(self,item): print(item, end="") return self def __str__(self): return "" print(Foo().think.different.itcast) 10.python多线程和多进程详细总结 # python多进程 #!@Author TrueNewBee import time import os from multiprocessing import Process def func(args): print(args) print(54321) time.sleep(1) print("子进程:",os.getpid()) print('子进程的父进程:', os.getppid()) print(12345) if __name__ =="__main__": #如果函数需要传参,就可以如下,args=() <--这是一个元组一个参数得加, #函数名不加括号 p = Process(target=func,args=(54321,)) #注册 #p是一个进程对象,还没有启动进程 #异步(不是同时进行的) p.start() #开启了一个子进程 print('*'*10) print('父进程:',os.getpid()) #查看当前进程号 print('父进程的父进程:', os.getppid()) #其实就是pycharm进程数 #进程的生命周期 #主进程 从运行到运行结束 #子进程 从start()开始,函数运行完结束 #开启了子进程的主进程: #自己的代码如果长,等待自己的代码执行结束才结束, #子进程的执行时间长,主进程会在主进程代码执行完毕后等待子进程执行完毕后 主进程结束
10.多进程异步
import time from multiprocessing import Process #多进程打印* 异步 def func(filename,content): """定义一个函数执行打印""" print('*'*arg1) time.sleep(5) print('*'*arg2) if __name__ =="__main__": # 异步,并不是按顺序同时打印 # for i in range(10): # # p = Process(target=func,args=(10*i,20*i)) # # p.start() p_list = [] #创建一个进程对象的列表 for i in range(10): p = Process(target=func,args=(10*i,20*i)) p_list.append(p) p.start() #让"运行完了最后打印,是个进程都是异步,在此之前所有子进程全部进行完了,然后才是同步 # for p in p_list:p.join() 列表推导式还原,其实就是一个for循环 [p.join() for p in p_list] #列表推导式 print("运行完了") import time from multiprocessing import Process def func(arg1,arg2): """定义一个函数执行打印""" print('*'*arg1) time.sleep(5) print('*'*arg2) # 先打印了 "运行完了" 5秒 再打印 arg2 实现了异步效果 if __name__ =="__main__": p = Process(target=func ,args=(10,20)) p.start() print("hhhhhhh") #join()和start()之间的部分仍然是异步,在join()下面才是同步 # p.join() #是感知一个子进程的结束,将异步的程序变为同步 """实现了异步效果""" print('========:运行完了')
11.python进程锁(Demo)
#锁 # 火车票 import json import time from multiprocessing import Process from multiprocessing import Lock def show(i): with open('ticket') as f: dic = json.load(f) print('余票: %s'%dic['ticket']) def buy_ticket(i,lock): lock.acquire() #拿钥匙进门 with open('ticket') as f: dic = json.load(f) time.sleep(0.1) if dic['ticket'] > 0 : dic['ticket'] -= 1 print('\033[32m%s买到票了\033[0m'%i) else: print('\033[31m%s没买到票\033[0m'%i) time.sleep(0.1) with open('ticket','w') as f: json.dump(dic,f) lock.release() # 还钥匙 if __name__ == '__main__': for i in range(10): p = Process(target=show,args=(i,)) p.start() lock = Lock() for i in range(10): p = Process(target=buy_ticket, args=(i,lock)) p.start()
12.进程 信号量
回顾:
多进程代码
from multiprocessing import Process
方法
进程对象.start() 开启一个子进程
进程对象.join() 感知一个子进程的结束
进程对象.terminate() 结束一个子进程
进程对象.is_alive() 查看某个子进程是否还在运行
属性
进程对象.name 进程名
进程对象.pid 进程号
进程对象.daemob 值为True的时候,表现新的子进程是一个守护进程
守护进程 随着主进程代码的执行结束而结束
一定在start之前设置
rom multiprocessing import Lock l = Lock() l.acquire() #拿钥匙 # 会造成数据不安全的操作 l.release() #换钥匙 # 信号量 # 多进程中的组件 # 一套资源 同一时间 只能被n个人访问 # 某一段代码 同一时间 只能被n个进程执行 import time import random from multiprocessing import Process from multiprocessing import Semaphore def ktv(arg, sem1): sem1.acquire() # 获取钥匙 """模拟唱歌""" print("%s走进ktv"%arg) time.sleep(random.randint(1, 5)) print("%s走出ktv"%arg) sem.release() if __name__ == '__main__': sem = Semaphore(4) for i in range(20): p = Process(target=ktv, args=(i, sem)) p.start()
13.事件
事件:通过一个信号 来控制 多个进程 同时 执行或者阻塞
一个信号可以使所有的进程都进入阻塞状态
也可以控制所有进程的解除阻塞
一个事件被创建之后,默认是阻塞状态
set 和 clear
分别用来修改一个事件的状态 True或者False
is_set 用来查看一个事件的状态
wait 是根据事件的状态来解决自己是否在wait处阻塞
False阻塞 True不阻塞
from multiprocessing import Event def event(): e = Event() # 创建一个事件 print(e.is_set()) # 查看一个事件的状态,默认设置成阻塞 e.set() # 将这个事件的状态改为True print(e.is_set()) e.wait() # 是依据e.is_set()的值来决定是否阻塞 print(123456) e.clear() # 将这个事件的状态改为False print(e.is_set()) e.wait() # 等待事件的信号被编程True print('*'*10) if __name__ == '__main__': event() # 红绿灯事件 # 用事件控制信号,控制进程 import time import random from multiprocessing import Process from multiprocessing import Event def cars(e2, i1): """创建一个车""" if not e2.is_set(): print("car%i1在等待" % i1) e2.wait() # 阻塞,直到得到一个 事件状态变成True的信号 print("car%s通过" % i1) def light(e1): """灯是独立的进程""" while True: if e1.is_set(): time.sleep(2) e1.clear() print("绿灯亮了") else: e1.set() print("红灯亮了") time.sleep(2) if __name__ == '__main__': e = Event() traffic = Process(target=light, args=(e, )) traffic.start() for i in range(20): car = Process(target=cars, args=(e, i)) car.start() time.sleep(random.random())
14.队列
import time from multiprocessing import Queue q = Queue(5) q.put(1) # 向队列里面放值 q.put(2) q.put(3) q.put(4) q.put(5) # 如果队列满了再添加则出现堵塞 print(q.full()) # 队列是否满了 print(q.get()) # 取出来数 print(q.get()) print(q.get()) print(q.get()) print(q.get()) # 如果队列全取出了再去将出现阻塞 print(q.empty()) # 判断队列是否为空 while True: """检查队列是否为空""" try: q.get_nowait() except : print("队列已空") time.sleep(1) from multiprocessing import Process from multiprocessing import Queue # 队列生产和消费数据 def produce(q1): """队列存储数据""" q1.put('hello') def consume(q2): """队列消费数据""" print(q2.get()) if __name__ == '__main__': q = Queue() p = Process(target=produce,args=(q, )) p.start() c = Process(target=consume, args=(q, )) c.start()
15.生产者和消费者 joinableQueue模型:
# 队列 # 生产者消费者模型 解决供需不平衡的问题 import time import random from multiprocessing import Process, JoinableQueue def consumer(name, q1): """负责消费生产的东西""" while True: food = q1.get() if food is None: # 判断为空则停止这个循环 print('%s获取到一个空' % name) break print('\033[31m%s消费了%s\033[0m' % (name, food)) time.sleep(random.randint(0, 2)) q1.task_done() # 提交回执 count - 1 def producer(name, food, q1): """负责生产包子""" for i in range(10): time.sleep(random.randint(0, 2)) f = '%s生产了%s%s' % (name, food, i) print(f) q1.put(f) q1.join() # 阻塞,直到一个队列中的数据 全部被执行完毕 if __name__ == '__main__': q = JoinableQueue(20) p1 = Process(target=producer, args=('Egon', '包子', q)) p2 = Process(target=producer, args=('WuSir', '泔水', q)) c1 = Process(target=consumer, args=('jinBoss', q)) c2 = Process(target=consumer, args=('alex', q)) p1.start() p2.start() c1.daemon = True # 成为守护进程,主进程中的代码执行完毕之后,子进程自动结束 c2.daemon = True c1.start() c2.start() p1.join() # 感知一个进程结束 p2.join() # 在消费者这一端: # 每次获取一个数据 # 处理一个数据 # 发送一个记号:标志一个数据被处理成功 # 在生产者这一端 # 每一次生产一个数据 # 且每一次生产的数据放在队列中 # 在队列中刻上一个记号 # 当生产者全部生产完毕后 # join信号: 已经停止生产数据了 # 且要等待之前被刻上的记号都被消费完 # 当数据都被处理完时,join阻塞结束 # consumer 中把所有的任务消耗完 # produce 端的join感知到,停止阻塞 # 所有的producer进程结束 # 主进程中的p.join结束 # 守护进程(消费者进程)结束
16.生产者和消费者 Queue模型:
# 队列 # 生产者消费者模型 解决供需不平衡的问题 import time import random from multiprocessing import Queue, Process def consumer(name, q1): """负责消费生产的东西""" while True: food = q1.get() if food is None: # 判断为空则停止这个循环 print('%s获取到一个空' % name) break print('\033[31m%s消费了%s\033[0m' % (name, food)) time.sleep(random.randint(0, 2)) def producer(name, food, q1): """负责生产包子""" for i in range(10): time.sleep(random.randint(0, 2)) f = '%s生产了%s%s' % (name, food, i) print(f) q1.put(f) if __name__ == '__main__': q = Queue(20) p1 = Process(target=producer, args=('Egon', '包子', q)) p2 = Process(target=producer, args=('WuSir', '泔水', q)) c1 = Process(target=consumer, args=('jinBoss', q)) c2 = Process(target=consumer, args=('alex', q)) p1.start() p2.start() c1.start() c2.start() p1.join() # 感知一个进程结束 p2.join() q.put(None) q.put(None)
17.复习总结内容:
复习:
信号量 Semaphore
from multiprocessing import Semaphore
用锁的原理实现的,内置了一个计数器
在同一时间,只能有指定数量的进程执行某一段被控制的代码
事件
wait 阻塞收到时间状态控制的同步组件
状态 True False is_set
true->false clear()
false->true set()
wait 状态为True不阻塞 状态为False的时候阻塞
上面都是为了同步!!!(让代码按顺序执行)
队列
Queue
put 当队列满的时候阻塞等待队列有空位置
get 当队列空的时候阻塞等待队列有数据
full empty 不完全准确
JoinableQueue
get task_done 这两个联用
put join 这两个联用
18. python进程间数据共享
# 管道 # 数据共享 Manager # 进程池和回调函数 ! # 管道 实现两进程间的信息传递 from multiprocessing import Pipe, Process # 一种使用管道方式 def func(conn3, conn4): conn4.close() # 把多余通道关闭 while True: try: msg = conn3.recv() # 接收消息 print(msg) except EOFError: # 没有数据可以取的时候抛出异常 conn3.close() break if __name__ == '__main__': conn1, conn2 = Pipe() # 接收两个参数 两个管道 Process(target=func, args=(conn1, conn2)).start() conn1.close() # 关闭多余通道 for i in range(20): conn2.send('吃了吗') # 发送消息 conn2.close()
# pipe 数据不安全性 这是一个例子,下面有另外一个解决方案的代码加上锁! # IPC import time import random from multiprocessing import Process, Pipe def producer(con1, pro1, name, food): """通过管道把生产东西传给消费者""" con1.close() for i in range(4): time.sleep(random.randint(1, 3)) f = '%s生产%s%s' % (name, food, i) print(f) pro1.send(f) pro1.close() def consumer(con2, pro2, name1): pro2.close() while True: try: food = con2.recv() print('%s吃了%s' % (name1, food)) time.sleep(random.randint(1, 3)) except EOFError: con2.close() break if __name__ == '__main__': con, pro = Pipe() p = Process(target=producer, args=(con, pro, 'a', '泔水')) c1 = Process(target=consumer, args=(con, pro, 'b')) c2 = Process(target=consumer, args=(con, pro, 'c')) c3 = Process(target=consumer, args=(con, pro, 'd')) p.start() c1.start() c2.start() c3.start() con.close() pro.close()
# 加上锁解决pipe数据不安全问题 from multiprocessing import Process, Pipe, Lock def consumer(p, name, lock1): produce1, consume1 = p produce1.close() while True: lock1.acquire() food=consume1.recv() lock.release() if food: print('%s 收到包子:%s' %(name,baozi)) else: consume1.close() break def producer(p, n): produce2, consume2=p consume2.close() for i in range(n): produce2.send(i) produce2.send(None) produce2.send(None) produce2.close() if __name__ == '__main__': produce,consume=Pipe() lock = Lock() c1 = Process(target=consumer, args=((produce, consume), 'c1', lock)) c2 = Process(target=consumer, args=((produce, consume), 'c2', lock)) p1 = Process(target=producer, args=((produce, consume), 10)) c1.start() c2.start() p1.start() produce.close() consume.close() c1.join() c2.join() p1.join() print('主进程')
加锁来控制操作管道的行为 来避免进程之间争抢数据造成的数据不安全现象
队列 进程之间数据安全的
管道 + 锁
牺牲效率 保障了数据安全
from multiprocessing import Manager, Process, Lock def main(dic1, lock1): lock1.acquire() # 加上所也是为了数据不安全问题 dic1['count'] -= 1 lock1.release() if __name__ == '__main__': m = Manager() lock = Lock() dic = m.dict({'count': 100}) p_list = [] for i in range(50): p = Process(target=main, args=(dic, lock)) p.start() p_list.append(p) for i in p_list: p.join() print('主进程:', dic)
19. python进程池
主要方法 常用的就是 from multiprocessing import Pool map() 同步 apply() 同步 apply_async() 异步 手动 close() join() 学的逐渐的由浅入深 为什么会有进程池的概念 效率 每开启进程,开启属于这个进程的内存空间 寄存器 堆栈 文件 进程过多 操作系统的调度 进程池 python中的 先创建一个属于进程的池子 这个池子指定能放多少个进程 先将这些进程创建好 更高级的进程池 可以根据用户需求改变进程数量 自带join方法,里面是异步 map(func,range) 里面传入的参数只能传入可迭代的 range ,列表,字典等
import time from multiprocessing import Pool, Process def func(n): for a in range(10): print(n+1) if __name__ == '__main__': start = time.time() pool = Pool(5) # 5个进程 pool.map(func, range(100)) # 100个任务 异步 t1 = time.time() - start p_list = [] for i in range(100): p = Process(target=func, args=(i, )) p_list.append(p) p.start() for i in p_list:p.join() t2 = time.time()-start print(t1, t2) # apply() 方法为同步的 # apply_async() 方法为异步的 一般都是用这个 import time import os from multiprocessing import Pool def func(n): print('start func%s' % n, os.getpid()) time.sleep(1) print('end func%s' % n, os.getpid()) if __name__ == '__main__': p = Pool(5) for i in range(10): # p.apply(func, args=(i, )) # 该方法为同步的 p.apply_async(func, args=(i,)) # 该方法为异步的 # 使用apply_async 必须加的两句话 p.close() # 结束进程接收任务 p.join() # 感知进程池中的任务执行结束 # p = Pool() # p.map(funcName, iterable) 默认异步的执行任务,且自带close和join # p.apply 同步调用的 # p.apply_async 异步调用 和主进程完全异步 需要手动close 和join import time from multiprocessing import Pool def func(i1): time.sleep(0.5) return i1*i1 if __name__ == '__main__': p = Pool() # res_list = [] # 储存res对象 到后面一块被接收 # for i in range(10): # res = p.apply_async(func, args=(i, )) # apply_async的结果就是func的返回值 # res_list.append(res) # # res.get() # get() 等着func的计算结果,阻塞了(同步) # for res in res_list: # print(res.get()) # 每五个返回一次数据 让get()变成了异步 # map() ret = p.map(func, range(100)) print(ret) # 整体返回所有数据
20. 回调函数
import os from multiprocessing import Pool def func1(n): print('in func1',os.getpid()) return n*n def func2(nn): print('in func2', os.getpid()) print(nn) if __name__ == '__main__': print('主进程:', os.getpid()) # 回调函数在主进程中执行的 p = Pool(5) for i in range(10): p.apply_async(func1, args=(10, ), callback=func2) # 回调func2返回值编程参数传给func1 p.close() p.join()
21.进程池方式的 socket
server端
# 用进程池子方法让服务端接就收多个客户端发来消息 # apply_async() 异步方法 import socket from multiprocessing import Pool def func(conn1): conn1.send(b'hello') print(conn.recv(1024).decode('utf-8')) conn1.close() if __name__ == '__main__': p = Pool(5) sk = socket.socket() sk.bind(('127.0.0.1', 8080)) sk.listen() while True: # 需要异步 多进程就收客户端发来消息 conn, add = sk.accept() p.apply_async(func, args=(conn, )) sk.close()
client 端
import socket sk = socket.socket() sk.connect(('127.0.0.1', 8080)) ret = sk.recv(1024).decode('utf-8') print(ret) msg = input('>>>').encode('utf-8') sk.send(msg) sk.close()
22. 上述复习和总结:
22.1: 进程池所有内容: 管道 数据的共享 Manager dic list 进程池 cpu个数+1 ret = map(func, iterable) 异步 自带close和join 所有结果的[] apply() 同步的 : 只有当func执行完之后,才会继续向下执行其他的代码 apply(func, args=()) 返回值就是func的return apply_async 异步的:当func被注册进入一个进程之后,程序就继续向下执行 apply_async(func, args()) 返回值: apply_async返回的对象 为了用户能从中获取func的返回对象 obj.get() get会阻塞直到对应的func执行完毕拿到结果 使用apply_async给进程池分配任务,需要线close()后join来保持多进程和主进程代码的同步性
22.2:回调函数 # 回调函数 from multiprocessing import Pool def func1(n): return n+1 def func2(m): print(m) if __name__ == '__main__': p = Pool(5) for i in range(10, 20): p.apply_async(func1, args=(i, ), callback=func2) p.close() p.join()
22.3线程:
import os import time from threading import Thread """多线程并发,都在同一个进程运行""" def func(n): time.sleep(1) # 全部线程并发睡1s 然后打印 print(n, os.getpid()) print('主线程:', os.getpid()) for i in range(10): t = Thread(target=func, args=(i, )) t.start() """使用面向对象方式开启线程""" class MyTread(Thread): def __init__(self, arg): super().__init__() self.arg = arg def run(self): time.sleep(1) print(self.arg) t = MyTread(10) t.start()
threading模块:
multiprocessing模块的完全模仿threading模块的接口,
二者在使用层面上有很大的相似地方
修改全局变量
在同一个进程多个线程之间的数据是共享的
def func1(a): global g g = 0 print(g, a, os.getpid()) g = 100 t_list = [] for i in range(10): t = Thread(target=func1, args=(i, )) t.start() t_list.append(t) for t in t_list: t.join() print(g)
进程 是最小的内存分配单位
线程 是操作系统调度的最小单位
线程被cpu执行了
进程内至少含有一个线程
进程中可以开启多个线程
开启一个线程所需要的时间要远远小于开启一个进程
多个线程内部有自己的数据栈,数据不共享
全局变量在多个线程之间是共享的
在CPython解释器下的python程序 在同一时刻 多线程只能有一个线程cpu被执行
高CPU(用多进程处理): 计算类 -----高CPU利用率 不占优势
高IO(用多线程处理): 爬取网页 200个网页
qq聊天 send recv
处理日志文件 读文件
处理web请求
读取数据库 写数据库
import time from threading import Thread from multiprocessing import Process """多线程与多进程时间对比""" def func(n): n+1 if __name__ == '__main__': start = time.time() t_list = [] for i in range(100): t = Thread(target=func, args=(i, )) t.start() t_list.append(t) for t in t_list: t.join() t1 = time.time() - start start1 = time.time() p_list = [] for i in range(100): p = Process(target=func, args=(i, )) p.start() p_list.append(t) for p in p_list: p.join() t2 = time.time() - start1 print(t1, t2)
22.4线程模块中其他方法
import threading import time def func(n): time.sleep(0.5) # 查看线程名字和id print(n, threading.current_thread(), threading.get_ident()) for i in range(10): threading.Thread(target=func, args=(i, )).start() print(threading.current_thread()) print(threading.active_count()) # 查看所有线程数 11 加上主线程 print(threading.enumerate())
23. 多线程写 socket sever
sever端
import socket from threading import Thread def chat(conn1): conn1.send(b'hello') msg = conn1.recv(1024).decode('utf-8') print(msg) inp = input(">>").encode('utf-8') conn1.send(inp) conn1.close() if __name__ == '__main__': sk = socket.socket() sk.bind(('127.0.0.1', 8080)) sk.listen() while True: conn, add = sk.accept() # 创建一个多线程实现多线程通讯 Thread(target=chat, args=(conn, )).start() sk.close()
client端 (多线程中可以用input 而多进程中不可以用input)
import socket sk = socket.socket() sk.connect(('127.0.0.1', 8080)) msg = sk.recv(1024) print(msg) inp = input('>>>>').encode('utf-8') # 多线程可以用input,多进程不可以用 sk.send(inp) sk.close()
25. 还是复习和总结:
25.1.复习:
正确的学习方法 input output correct 纠正 线程: 1.线程是进程中的执行单位 2.线程是cpu执行的最小单位 3.线程之间资源共享 4.线程的开启和关闭以及切换的时间开销远远小于进程 5.线程本身可以在同一时间使用多个cpu python 与 线程 CPython解释器在解释代码过程中容易产生数据不安全的问题 GIL全局解释器锁 锁的是线程 threading
25.2.守护线程:
import time from threading import Thread def func1(): while True: print('*'*10) time.sleep(1) def func2(): print('in func2') time.sleep(5) if __name__ == '__main__': t = Thread(target=func1, ) t.daemon = True t.start() t2 = Thread(target=func2, ) t2.start() t2.join() print('主线程') # 守护进程随着主进程代码的结束而结束(进程间资源不共享,所以想咋结束咋结束) # 守护线程会在主线程结束之后等待其他子线程的结束才结束(线程间资源共享,所以不能主线程不能立马结束) # 主进程在执行完自己的代码之后不会立即结束,而是等待子进程结束之后 挥手子进程的资源 # import time # from multiprocessing import Process # def func(): # time.sleep(5) # if __name__ == '__main__': # Process(target=func, ).start()
25.3.锁
# import time # from threading import Thread, Lock # def func(lock1): # global n # lock1.acquire() # 加上一个锁 # # n = 1 python内部就是下面执行的 # temp = n # time.sleep(0.2) # n = temp - 1 # 9 刚取回来还没来得及赋值又被别人拿走了,所以得自己加个锁不让被人拿走 # lock1.release() # 换钥匙 # # # n = 10 # t_list = [] # lock = Lock() # for i in range(10): # t = Thread(target=func, args=(lock, )) # t.start() # t_list.append(t) # for t in t_list: # t.join() # print(n) # 不加锁是9 加锁是 0 # 科学家吃面问题 经典死锁问题 # noodle_lock = Lock() # fork_lock = Lock() # 互斥锁 # # # def eat1(name): # noodle_lock.acquire() # print('%s拿到面条啦' % name) # fork_lock.acquire() # print('%s拿到叉子啦' % name) # print('%s吃面' % name) # fork_lock.release() # noodle_lock.release() # # # def eat2(name): # fork_lock.acquire() # print('%s拿到叉子啦' % name) # time.sleep(1) # noodle_lock.acquire() # print('%s拿到面条啦' % name) # print('吃面') # noodle_lock.release() # fork_lock.release() # # # if __name__ == '__main__': # Thread(target=eat1, args=('alex', )).start() # Thread(target=eat2, args=('Egon',)).start() # Thread(target=eat1, args=('bossJin',)).start() # Thread(target=eat2, args=('zeZha',)).start() import time from threading import RLock, Thread fork_lock = noodle_lock = RLock() # 一个钥匙串上的两把钥匙 # 递归锁 为了解决死锁问题,可以acquire()多次, def eat1(name): noodle_lock.acquire() # 一把钥匙 print('%s拿到面条啦' % name) fork_lock.acquire() print('%s拿到叉子啦' % name) print('%s吃面' % name) fork_lock.release() noodle_lock.release() def eat2(name): fork_lock.acquire() print('%s拿到叉子啦' % name) time.sleep(1) noodle_lock.acquire() print('%s拿到面条啦' % name) print('%s吃面' % name) noodle_lock.release() fork_lock.release() if __name__ == '__main__': Thread(target=eat1, args=('alex', )).start() Thread(target=eat2, args=('Egon',)).start() Thread(target=eat1, args=('bossJin',)).start() Thread(target=eat2, args=('zeZha',)).start()
25.4.条件和定时器
import time from threading import Semaphore, Thread def func(sem1, a, b): # 同一时间就让四个线程执行代码 sem1.acquire() time.sleep(1) print(a+b) sem1.release() if __name__ == '__main__': sem = Semaphore(4) for i in range(10): t = Thread(target=func, args=(sem, i, i+5)) t.start()
25.5.事件
# 事件被创建的时候 # False状态 # wait() 阻塞 # True状态 # wait() 非阻塞 # clear 设置状态为False # set 设置状态为True # 数据库- 文件夹 # 文件夹里有好多excel表格 # 1.能够更方便的对数据进行增删改查 # 2.安全访问的机制 # 伪代码 看现象: # 起两个线程 # 第一个线程:连接数据库 # 等待一个信号,告诉我我们之间的网络是通的 # 连接数据库 # 第二个线程:检测与数据库之间的网络情况是否连通 # time.sleep(0,2) # 将事件状态设置为True import time import random from threading import Thread, Event def connect_db(e1): count = 0 while count < 3: # 连接三次 e1.wait(0.5) # 状态是False的时候,我只等待1s if e1.is_set() == True: print('连接成功') break else: count += 1 print('第%s次连接失败' % count) else: # 连接三次都都没连上,主动抛出异常 raise TimeoutError('数据库连接超时') def check_web(e2): time.sleep(random.randint(0, 3)) e2.set() if __name__ == '__main__': e = Event() t1 = Thread(target=connect_db, args=(e, )) t2 = Thread(target=check_web, args=(e, )) t1.start() t2.start()
25.6.条件
# 条件 # 锁 # acquire release # 一个条件被创建之初 默认有一个False状态 # False状态 会影响wait()一直处于等待状态 # notify(int数据类型) 制造一串钥匙 # 不大重要,只能停留在面试中 from threading import Condition, Thread def func(con1, i1): con1.acquire() con1.wait() # 等钥匙 print('在第%s个循环里' % i1) con.release() con = Condition() for i in range(10): Thread(target=func, args=(con, i)).start() while True: num = int(input('>>>')) con.acquire() con.notify(num) # 造钥匙 con.release()
25.7.定时器
# 用的不多,知道这个组件就好了 import time from threading import Timer def func(): print('时间同步') if __name__ == '__main__': while True: Timer(5, func).start() # 非阻塞的 time.sleep(5)
25.8.队列
# queue import queue q = queue.Queue() # 队列先进先出 # q.put() # q.get() # q.put_nowait() # q.get_nowait() queue.LifoQueue() # 栈 先进后出 q.put((50, 'a')) q.put((30, 'r')) q.put((1, 'z')) q.put((1, 'd')) print(q.get())
25.9.池
import time from concurrent.futures import ThreadPoolExecutor def func(n): time.sleep(2) print(n) return n*n tpool= ThreadPoolExecutor(max_workers=5) # 默认 不要超过cpu个数*5 t_list = [] for i in range(20): t = tpool.submit(func, i) # 异步提交任务 t_list.append(t) tpool.shutdown() # close+join print('主线程') for t in t_list: print('****', t.result())
26. IO多路复用和协程复习
26.1协程:
进程 启动多个进程 进程之间是由操作系统负责调用
线程 启动多个线程 真正被CPU执行的最小单位实际是线程
开启一个线程 创建一个线程 寄存器 堆栈
关闭一个线程
协程
本质上是一个线程
能够在多个任务之间切换来节省一些IO时间
实现并发的手段
def consumer(): """创建一个生成器""" while True: x = yield print('处理了数据', x) def producer(): c = consumer() next(c) for i in range(10): print('生产了数据:', i) c.send(i) producer()
真正的协程模块就是使用greenlet完成的切换
from greenlet import greenlet def eat(): print('eating start') g2.switch() # 切换到play print('eating end') g2.switch() def play(): print('playing start ') g1.switch() print('playing end') if __name__ == '__main__': # 用于切换线程 g1 = greenlet(eat) g2 = greenlet(play) g1.switch()
放在开头,是为了识别time (IO)
from gevent import monkey; monkey.patch_all() import time import gevent import threading def eat(): print(threading.current_thread()) # 查看线程名字 print('eating start') time.sleep(1) # gevent 检测到停1s,则调到另外一个函数中 print('eating end') def play(): print(threading.current_thread()) print('playing start ') time.sleep(1) print('playing end') if __name__ == '__main__': g1 = gevent.spawn(eat) # 开启协程 g2 = gevent.spawn(play) g1.join() g2.join()
进程和线程的任务切换由操作系统完成
协程任务之间的切换由程序(代码)完成 只有遇到协程模块能识别的IO操作的时候,程序才会进行任务切换实现并发效果
同步 和 异步 (网络操作常用协程)
from gevent import monkey; monkey.patch_all() import time import gevent def task(): time.sleep(1) print(12345) def sync(): for i in range(10): task() def async(): g_list = [] for i in range(10): g = gevent.spawn(task) g_list.append(g) gevent.joinall(g_list) # for g in g_list :g.join() if __name__ == '__main__': sync() async()
协程 : 能够在一个线程中实现并发效果的概念
能够规避一些任务中的IO操作
在任务的执行过程中,检测到IO就切换到其他任务
多线程 被弱化了
协程: 在一个线程上,提高cpu的利用率
协程相比于多线程的优势 切换的效率更快了
爬虫例子(正则基础)
请求过程中的IO等待
from gevent import monkey;monkey.patch_all() import gevent from urllib.request import urlopen def get_url(url1): response = urlopen(url1) content = response.read().decode('utf-8') # 有各式的 return len(content) url = { 'http://www.baidu.com', 'http://www.taobao.com', 'http://www.hao123.com', } g_list = [] for i in url: g = gevent.spawn(get_url, i) g_list.append(g) gevent.joinall(g_list) for g in g_list: print(g.value) 26.2用协程写 socket_demo socket # 用协程写 socket # 用协程是最快最方便的 最省时间占用最小,代码间的转换 from gevent import monkey; monkey.patch_all() import socket import gevent def talk(conn1): conn1.send(b'hello') rec = conn.recv(1024).decode('utf-8') print(rec) conn.close() if __name__ == '__main__': sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() while True: conn, add = sk.accept() gevent.spawn(talk, conn) sk.close() client import socket sk = socket.socket() sk.connect(('127.0.0.1', 8080)) rec = sk.recv(1024).decode('utf-8') print(rec) msg = input('>>>>').encode('utf-8') sk.send(msg) sk.close()
26.3IO模型 笔记
同步 : 提交一个任务之后要等待这个任务执行完毕 异步 : 只管提交任务,不等待这个任务执行完毕就可以做其他事情 阻塞 : input urlopen() 在socket里面:recv() recvfrom() accept 非阻塞 : 除了阻塞的其他都是非阻塞 阻塞 线程 运行状态 --> 阻塞状态-->就绪 非阻塞 IO多路复用 select机制 Windows和linux 都是操作系统轮询每一个被监听的项,看是否读操作 poll机制 linux 它可以监听的对象比select机制可以监听的多 随着监听项的增多,导致效率降低 epoll机制 linux
26.4.非阻塞模型
socket
# 非阻塞IO模型 # 单线程中非阻塞!(没有用协程!) import socket sk = socket.socket() sk.bind(('127.0.0.1', 8080)) sk.setblocking(False) # 默认True阻塞, False非阻塞 sk.listen() conn_list = [] del_conn = [] # 存入失效连接的列表 while True: # 接收异常 BlockingIOError 完成非阻塞 try: conn, add = sk.accept() # 不阻塞,但没人连我会报错 print('建立连接了', add) # msg = conn.recv(1024) # 不阻塞,但没有消息会报错 # print(msg) conn_list.append(conn) except BlockingIOError: # 循环列表连接 看看是否有人发消息 for con in conn_list: try: msg = con.recv(1024) # 不阻塞,但没有消息会报错 if msg == b'': del_conn.append(con) # 把失效的连接存到del_conn中 continue print(msg) con.send(b'bye bye') except BlockingIOError: pass for con in del_conn: con.close() conn_list.remove(con) # 在conn_list中删除失效连接 del_conn.clear() # 清空删除列表
client
# 非阻塞IO 多线程并发socket IO import time import socket import threading def func(): sk = socket.socket() sk.connect(('127.0.0.1', 8080)) sk.send(b'hello') time.sleep(1) msg = sk.recv(1024) print(msg) sk.close() for i in range(20): threading .Thread(target=func).start()
26.5.IO多路复用
socket
# IO多路复用 多并发! import select import socket sk = socket.socket() sk.bind(('127.0.0.1', 8080)) sk.setblocking(False) sk.listen() read_list = [sk] # 储存监听对象 while True: # [sk, conn] sk,发送链接 conn监听发送消息 r_list, w_list, x_list = select.select(read_list, [], []) for i in r_list: if i is sk: conn, add = i.accept() # 没有sk, 有conn则会报错 read_list.append(conn) else: ret = i.recv(1024) if ret == b'': i.close() read_list.remove(i) continue print(ret) i.send(b'goodbye')
client
import socket import threading import time def func(): sk = socket.socket() sk.connect(('127.0.0.1', 8080)) sk.send(b'hello') time.sleep(1) sk.recv(1024) sk.close() for i in range(20): threading .Thread(target=func).start()
26.6selector_dome
# 服务端 from socket import * import selectors sel = selectors.DefaultSelector() def accept(server_fileobj, mask): conn, addr = server_fileobj.accept() sel.register(conn, selectors.EVENT_READ, read) def read(conn, mask): try: data = conn.recv(1024) if not data: print('closing', conn) sel.unregister(conn) conn.close() return conn.send(data.upper()+b'_SB') except Exception: print('closing', conn) sel.unregister(conn) conn.close() sk = socket(AF_INET, SOCK_STREAM) sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sk.bind(('127.0.0.1', 8088)) sk.listen(5) sk.setblocking(False) # 设置socket的接口为非阻塞 # 相当于网select的读列表里append了一个文件句柄server_fileobj,并且绑定了一个回调函数accept sel.register(sk, selectors.EVENT_READ, accept) # 说白了就是,如果有人请求连接sk,就调用accept方法 while True: events = sel.select() # 检测所有的sk,conn,是否有完成wait data的 for sel_obj, mask in events: # [sk] callback = sel_obj.data # callback = accept callback(sel_obj.fileobjmask) # accept(server_fileobj,1)
#客户端 # 基于selectors模块实现聊天 from socket import * c=socket(AF_INET, SOCK_STREAM) c.connect(('127.0.0.1',8088)) while True: msg=input('>>: ') if not msg:continue c.send(msg.encode('utf-8')) data = c.recv(1024) print(data.decode('utf-8'))
2018-9-23 16:18:40 终于整理完了
下次开始整理数据库内容 目前已经把博客python知识整理完了!好有成就感!
下次整理 7.26数据库 (1)