多进程介绍

实验发现不涉及IO输入的多线程,串行运行在老的(新解释器缩短差距)解释器有时候比多线程快,这是什么原因?

GIL:全局解释锁(这玩意跟python语言无关,跟解释种类有关,只对CPython解释器有用,但是这种站主导市场)
       因为有GIL,所以同一时刻,只有一个线程被一个CPU执行
        多核对于Python多线程用不上,完了!!!
        多进程可以用上多核,但是进程的开销大!!

总结:多线程用不上多核(计算密集型任务时,不如串行;IO密集型任务它还是不错的),多进程太多时开销大,所以用多进程+协程。

 1 #!/usr/bin/env python
 2 #-*-coding:utf-8 -*-
 3 
 4 '''
 5 python 大都编辑器的单一进程中的多线程是无法用多核的,因为GIL;但是多进程可以,所以多进程模块multiprocessing
 6 线程的调用两种方法,一是直接用treading模块,二是继承这个模块。进程相似(守护进程deamon有点区别)
 7 如果有4个CPU,开4个进程,就可以实现真的并行,而不是并发
 8 '''
 9 
10 import time, multiprocessing
11 import threading
12 
13 def f(name):
14     time.sleep(1)
15     print("hello",name,time.ctime())
16 
17 if __name__ == '__main__':
18     plist = []
19     for i in range(2):
20         p = multiprocessing.Process(target=f,args=("wan",))
21         # p = threading.Thread(target=f,args=('wan',))
22         plist.append(p)
23         p.start()
24 
25     for i in plist:
26         i.join()
27 
28     print("ending........")
 1 from multiprocessing import Process
 2 import os
 3 import time
 4 
 5 
 6 def info(title):
 7     print("title:", title)
 8     print('parent process:', os.getppid())  # 每个进程有一个ID号,getppid是得到父进程id
 9     print('process id:', os.getpid())       # getpid是得到自己当前运行的id
10 
11 def f(name):
12 
13     info('function f')
14     print('hello', name)
15 
16 
17 if __name__ == '__main__':
18 
19     info('main process line')
20 
21     time.sleep(1)
22     print("------------------")
23     p = Process(target=info, args=('yuan',))     # 这里的父进程的ID号应该是等于上面info调用的后等到的process id 一样
24     p.start()
25     p.join()
View Code

 介绍一下multiprocessing.Process类:

        is_alive() 返回进程是否运行

        terminate () 不管任务是否完成,立即停止工作进程

进程之间的通讯,因为一个进程中的多个线程是在同一块进程的内存空间中,他们数据共享,很简单。但是进程之间的内存空间不同,要解决进程之间的通讯,以及共享数据问题(我改了数据,你拿到的应该是我改后的)。进程中涉及大量的数据拷贝,所以资源消耗大

下面讲进程通讯:队列,管道(前两个只是完成通讯功能,没有实现数据共享),Managers(可实现数据共享)

队列:

         说队列前,先介绍一下列表这种数据结构,看看它在线程中的缺陷,线程不安全。

 1 import threading,time
 2 
 3 li = [1, 2, 3, 4, 5]
 4 
 5 def pri():    # 每个线程删除列表中的最后一个元素
 6     while li:
 7         a = li[-1]
 8         print(a)
 9         time.sleep(1)
10         li.remove(a)
11 
12 t1 = threading.Thread(target=pri)
13 t2 = threading.Thread(target=pri)
14 
15 t1.start()
16 t2.start()
17 
18 t1.join()
19 t2.join()
20 print("end.............")
View Code

       用了队列,刚才列表在线程中的问题就不存在了。

 1 import queue
 2 
 3 q = queue.Queue(3) # 先进先出,后进先出是.LifeoQueue,还可以按级别出.PriorityQueue
 4 
 5 q.put(12)          # put 与 get还有一些其他的参数
 6 q.put("hello")
 7 q.put({'name':"wan"})
 8 
 9 print(q.qsize())       # 队列中实际有多少值
10 print(q.empty())       # 是否为空
11 print(q.full())        # 是否为满
12 
13 while 1:
14     data = q.get()
15     print(data)
16     print("--------------")
View Code

      同样,用队列进行进程中的通讯如下:

 1 import multiprocessing
 2 # import queue
 3 
 4 
 5 def foo(q):
 6 
 7     q.put(123)
 8     q.put("wan")
 9 
10 if __name__ == '__main__':
11     q = multiprocessing.Queue()               # 进程中有自己的队列
12     plist = []
13     for i in range(3):
14         p = multiprocessing.Process(target=foo,args=(q,))
15         # 这里必须把参数q传进去,因为它不像线程那样共享,在主线程建立q,只是主进程内存空间中,子进程中队列都没定义
16         plist.append(p)
17         p.start()
18     print(q.get())
19     print(q.get())
20     print(q.get())
21     print(q.get())
multiprocessing.Queue

管道通讯:

 1 from multiprocessing import Process, Pipe
 2 def f(conn):
 3     conn.send([12, {"name":"yuan"}, 'hello'])       # 子进程发
 4     response=conn.recv()                            # 子进程收
 5     print("response",response)
 6     conn.close()
 7     # print("q_ID2:",id(conn))
 8 
 9 if __name__ == '__main__':
10 
11     parent_conn, child_conn = Pipe()                 # 双向管道,类似电话的两头,一头给子进程,一头给主进程
12 
13     # print("q_ID1:",id(child_conn))
14     p = Process(target=f, args=(child_conn,))
15     p.start()
16 
17     print(parent_conn.recv())   # 父进程(主进程)接收到的
18     parent_conn.send("儿子你好!") # 父进程发
19     p.join()
multiprocessing.Pipe

Manager:

 1 from multiprocessing import Process, Manager
 2 
 3 def f(d, l,n):
 4 
 5     d[n] = '1'    #{0:"1"}
 6     d['2'] = 2    #{0:"1","2":2}
 7 
 8     l.append(n)    #[0,1,2,3,4,   0,1,2,3,4,5,6,7,8,9]
 9     #print(l)
10 
11 
12 if __name__ == '__main__':
13 
14     with Manager() as manager:        # 类似用with打开文件,省略关的功能
15 
16         d = manager.dict()            #{}
17 
18         l = manager.list(range(5))     #[0,1,2,3,4]
19 
20 
21         p_list = []
22 
23         for i in range(10):
24             p = Process(target=f, args=(d,l,i))
25             p.start()
26             p_list.append(p)
27 
28         for res in p_list:
29             res.join()
30 
31         print(d)
32         print(l)
View Code
#!/usr/bin/env python
#-*-coding:utf-8 -*-

'''
线程有同步(同步锁):因为它们共享数据,可能会出错;
进程数据不共享,但是也共享一些资源(比如屏幕(进程1与进程2可能同一时刻想同时输出在屏幕上,也就是出现串行错误))
因此,进程也需要同步multiprocessing.Lock

'''

from multiprocessing import Process,Lock

def f(l,i):
    l.acquire()
    print('hello world %s'%i)
    l.release()

if __name__ == '__main__':
    lock = Lock()

    for num in range(12):
        Process(target=f,args=(lock, num)).start()

    print('ending.............')
 1 '''
 2 进程池:控制开进程的最大量 multiprocessing.Pool
 3 进程池两个方法:apply同步 , apply_async异步
 4 
 5 回调函数:就是某个动作或者某个函数执行成功后再去执行的函数
 6         感觉回调函数可以把内容写在它跟的函数后面,这样基本就等同了
 7         但是callback是在主进程下面调用的
 8 '''
 9 
10 from multiprocessing import Process,Pool
11 import time,os
12 
13 def f(i):
14     time.sleep(3)
15     print(i)
16     print("son pid: ",os.getpid())
17     return 'hello %s'%i
18 
19 def Bar(arg):                  # 回调函数必须至少有一个参数,就是它跟的函数的返回值
20     print(arg)
21     print('callback pid: ',os.getpid())
22 
23 if __name__ == '__main__':
24     pool = Pool(5)
25     print("main pid:  ",os.getpid())
26     for i in range(100):
27         pool.apply_async(func=f,args=(i,),callback=Bar)       # callback是回调函数名字,它在每个子进程执行完后执行
28 
29     pool.close()
30     pool.join()             # 注意这个pool.close与pool.join的顺序是固定的
原文地址:https://www.cnblogs.com/maxiaonong/p/9538422.html