Python 3学习 ——Python 多进程、协程、编码
Python 学习——Python 多进程、协程、编码
写此博客 是为了激励自己,并且将自己的心得以及遇到的问题与人分享
一、进程
1.概述
multiprocessing 包是 Python 中的多进程管理包。与 thread.Threading 类似,可以利用 multiprocessing 对象来创建一个进程。该 Processing 对象与 Thread 对象的用法相同,也有 start() run() join() 的方法。具体使用看下面代码实现。使用这些 API 的时候有如下几点注意事项:
- 十分有必要对每个 Process 对象调用 join() 方法,阻止进程成为僵尸进程的一个手段。在多线程中由于只有一个进程,所以不需要采用这种手段。
- 两个进程之间不能够共享数据,如果想共享数据要有一个第三方来对两者进行沟通共享。优先使用 Pipe 和 Queue ,避免使用 Lock / Event / Semaphone / Condition 等同步方式。
- Windows 系统下,要想启动一个子进程,必须加上 If __name__ == "__main__": ,进程相关的要写在这句下面。
1 #author:"LFD" 2 #date: 2018/6/8 3 from multiprocessing import Process 4 import time 5 6 def f(name): 7 time.sleep(1) 8 print('hello',name,time.ctime()) 9 10 if __name__ == "__main__": 11 p_list = [] 12 for i in range(3): 13 p = Process(target=f,args=('liufeiduo',)) # 创建进程 14 p_list.append(p) 15 p.start() 16 for p in p_list: 17 p.join() 18 19 print('jincheng end!') 20 21 ''' # 运行结果: 22 hello liufeiduo Fri Jun 8 11:15:46 2018 23 hello liufeiduo Fri Jun 8 11:15:46 2018 24 hello liufeiduo Fri Jun 8 11:15:46 2018 25 jincheng end! 26 '''
上面是通过方法实现多进程;
下面是通过调用类实现多进程;
1 from multiprocessing import Process 2 import time 3 4 class MyProcess(Process): 5 def __init__(self,name): 6 super(MyProcess,self).__init__() 7 self.name = name 8 def run(self): 9 time.sleep(1) 10 print('hello',self.name,time.ctime()) 11 12 if __name__ == '__main__': 13 p_list = [] 14 for i in range(3): 15 p = MyProcess('liufeiduo') 16 p.start() 17 p_list.append(p) 18 19 for p in p_list: 20 p.join() 21 22 print('end')
2.进程关系
1 from multiprocessing import Process 2 import os,time 3 4 def info(title): 5 print(title) 6 print('module name:',__name__) 7 print('parent process:',os.getppid()) 8 print('process id:',os.getpid()) 9 10 def f(name): 11 pass 12 13 if __name__ == '__main__': 14 info('\033[32;1mmain process line\033[0m') 15 time.sleep(5) 16 p = Process(target=info,args=('bob',)) 17 p.start() 18 p.join() 19 20 21 22 ''' 运行结果: 23 main process line 24 module name: __main__ 25 parent process: 7708 7708 是Pycharm 在电脑上分得的进程ID 26 process id: 13972 27 bob 28 module name: __mp_main__ 29 parent process: 13972 30 process id: 6024 31 32 '''
3.实现不同进程间的通信
通过队列实现 ( queue ) :
1 #author:"LFD" 2 #date: 2018/6/9 3 4 from multiprocessing import Process,Queue 5 import time 6 def f(q): 7 q.put([42,2,'hello',]) 8 print('main q id:', id(q)) 9 10 11 if __name__ == '__main__': 12 q = Queue() 13 p_list = [] 14 print('main q id:',id(q)) 15 16 for i in range(3): 17 p = Process(target=f,args=(q,))#args=(q,) 把q当作参数传入到子进程当中 就可以找到了 18 p_list.append(p) 19 p.start() 20 21 print(q.get()) # 两个进程间数据相互独立 是取不出来的 22 print(q.get()) 23 print(q.get()) 24 25 ''' 执行结果: 26 main q id: 1806769092704 27 main q id: 2077092271608 28 [42, 2, 'hello'] 29 main q id: 2579859953200 30 [42, 2, 'hello'] 31 main q id: 2635574990216 32 [42, 2, 'hello'] 33 '''
通过 Pipe 实现:
1 from multiprocessing import Process,Pipe 2 3 def f(conn): 4 conn.send('约么') 5 6 print(conn.recv()) 7 conn.close() 8 9 if __name__ == '__main__': 10 parent_conn,child_conn = Pipe() 11 p = Process(target=f,args=(child_conn,)) 12 p.start() 13 print(parent_conn.recv()) 14 15 parent_conn.send('约') 16 p.join() 17 18 19 20 ''' 运行结果: 21 约么 22 约 23 '''
4.数据共享—— Manager
1 from multiprocessing import Process, Manager 2 3 def f(d, l,n): 4 d[n] = '1' 5 d['2'] = 2 6 d[0.25] = None 7 l.append(n) 8 print(l) 9 10 if __name__ == '__main__': 11 with Manager() as manager: 12 d = manager.dict() 13 14 l = manager.list(range(5)) 15 p_list = [] 16 for i in range(10): 17 p = Process(target=f, args=(d, l,i)) 18 p.start() 19 p_list.append(p) 20 for res in p_list: 21 res.join() 22 23 print(d) 24 print(l) 25 26 27 ''' 执行结果: 28 [0, 1, 2, 3, 4, 0] 29 [0, 1, 2, 3, 4, 0, 1] 30 [0, 1, 2, 3, 4, 0, 1, 2] 31 [0, 1, 2, 3, 4, 0, 1, 2, 3] 32 [0, 1, 2, 3, 4, 0, 1, 2, 3, 4] 33 [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5] 34 [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6] 35 [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7] 36 [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8] 37 [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 38 {0: '1', '2': 2, 0.25: None, 1: '1', 2: '1', 3: '1', 4: '1', 5: '1', 6: '1', 7: '1', 8: '1', 9: '1'} 39 [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
二、协程
协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程。
优点:
- 无需线程上下文切换的开销
- 无需原子操作锁定及同步的开销方便切换控制流,简化编程模型
- "原子操作(atomic operation)是不需要synchronized",所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。
- 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
缺点:
- 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
- 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序
1.通过 yeild 实现协程代码实例:
1 #author:"LFD" 2 #date: 2018/6/11 3 import time 4 import queue 5 6 7 def consumer(name): 8 print("--->starting eating baozi...") 9 while True: 10 new_baozi = yield 11 print("[%s] is eating baozi %s" % (name, new_baozi)) 12 # time.sleep(1) 13 14 15 def producer(): 16 r = con.__next__() 17 r = con2.__next__() 18 n = 0 19 while n < 5: 20 n += 1 21 print("\033[32;1m[producer]\033[0m is making baozi %s" % n) 22 con.send(n) 23 con2.send(n) 24 25 26 27 if __name__ == '__main__': 28 con = consumer("c1") 29 con2 = consumer("c2") 30 p = producer()
2.greenlet 是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator 。
1 from greenlet import greenlet 2 3 4 def test1(): 5 print(12) 6 gr2.switch()# 跳转到 test2 去执行 switch 方法实现切换 7 print(34) 8 gr2.switch()# 再次跳转到test2 去执行了 9 10 11 def test2(): 12 print(56) 13 gr1.switch()# 跳转回 test1 执行 14 print(78) 15 16 17 gr1 = greenlet(test1) 18 gr2 = greenlet(test2) 19 gr1.switch()
3.Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。
1 import gevent 2 3 4 def func1(): 5 print('\033[31;1m李闯在跟海涛搞...\033[0m') 6 gevent.sleep(2) 7 print('\033[31;1m李闯又回去跟继续跟海涛搞...\033[0m') 8 9 10 def func2(): 11 print('\033[32;1m李闯切换到了跟海龙搞...\033[0m') 12 gevent.sleep(1) 13 print('\033[32;1m李闯搞完了海涛,回来继续跟海龙搞...\033[0m') 14 15 16 gevent.joinall([ 17 gevent.spawn(func1), 18 gevent.spawn(func2), 19 # gevent.spawn(func3), 20 ])
三、Python2 和 Python3 的编码
编码:基本概念很简单。首先,我们从一段信息即消息说起,消息以人类可以理解、易懂的表示存在。我打算将这种表示称为“明
文”(plain text)。对于说英语的人,纸张上打印的或屏幕上显示的英文单词都算作明文。
其次,我们需要能将明文表示的消息转成另外某种表示,我们还需要能将编码文本转回成明文。从明文到编码文本的转换称为“编码”,从编码
文本又转回成明文则为“解码”。