python 3.x 多进程编程

多进程编程

  • 第一种方法:
    • 首选使用ProcessPoolExecutor进行多进程编程  
    • from concurrent.futures import ProcessPoolExecutor, as_completed  
    • 在ProcessPoolExecutor内部实现还是multiprocessing  
  • 第二种方法:
    • multiprocessing进行多进程编程  
    • multiprocessing比ProcessPoolExecutor更加底层.  
    • 只有了解了multiprocessing才能更好的了解ProcessPoolExecutor.  
  • Process类的方法
    • def run(self):  
    • def start(self):  
    • def terminate(self): 关闭进程  
    • def join(self, timeout=None):  
    • def is_alive(self): 判断进程是否运行.  
         return False
  • multiprocessing中的进程池
    • Pool(self, processes=None, initializer=None, initargs=(), maxtasksperchild=None, context=None):  
      • processes:线程池的数量(多少个进程)    
      • 如果processes为None,processes会通过os的cpu_count()来声明有多少个cpu,就会有多少个进程,因为对于多进程来说,进程数最好等于cpu的数量,这样性能是最高的,所以说processes的值给不给都可以.
    • apply_async()源码:  
      • def apply_async(self, func, args=(), kwds={}, callback=None, error_callback=None):    
            if self._state != RUN:
                raise ValueError("Pool not running")
            result = ApplyResult(self._cache, callback, error_callback)
            self._taskqueue.put(([(result._job, 0, func, args, kwds)], None))
            return result
      • ApplyResult类似于Future类    
    • imap()函数:  
      • 对应executor(线程池)里面的map()方法,输出结果与参数列表的顺序一致.    
    • imap_unordered()函数:  
      • 不按照加载列表的顺序输出结果,按照执行完毕并成功的先后顺序输出结果.    

 

通过一个小案例引出多进程编程

 1 import os
 2 import time
 3 
 4 # fork()函数可以新建一个子进程,fork()只能用于linux/unix下,在windows下是使用不了的
 5 # 创建的子进程行为和多线程的行为是不一样的
 6 # fork()函数实际上会返回两次.如果在主进程中,pid就会是子进程的一个id,如果pid等于0那么pid就是子进程.
 7 # 因为在fork()之后,实际上有两个进程存在,主进程和子进程,通对pid的判断来区分主进程和子进程.
 8 pid = os.fork()
 9 print("jayden")
10 
11 if pid == 0:
12     print("子进程: {}, 父进程: {}".format(os.getpid(), os.getppid()))
13 else:
14     print("我是父进程: {}".format(pid))
15 
16 # sleep的作用???
17 time.sleep(2)
18 
19 """
20 通过上面的代码,预想一下输出结果:
21     首先会print jayden,第二就是他会在主进程中运行,会打印我是父进程,这时我们预想的结果.
22     但他实际上会不会与我们预想的结果是一样的呢???
23 
24     在linux下运行代码,输出结果:
25         jayden
26         我是父进程:  24474
27         jayden
28         子进程: 24474 , 父进程: 24473
29         
30     输出的结果很奇怪,jayden运行了两次,将if...else语句中的两段代码都运行了,根据if...else语句的特点,只能执行两段代码中的
31     一段代码.为什么两段代码都执行了呢???这就涉及到进程和线程不一样的地方,一但运行了fork(),就会立马创建一个进程,
32     这个进程就是windows管理器中的进程.
33     1.fork一个子进程,但是他的父进程依然要向下进行,就会打印出jayden,然后判断条件只能存在一个,打印出我是父进程:  24474.
34     2.子进程会将父进程中的所有的数据原样拷贝到子进程中,包括我们的代码运行,所以说进程的数据和线程之间的数据是完全隔离的.
35     之前介绍,线程可以通过全局变量是可以通信的,但是进程通过全局变量是不能通信的,因为进程的整个数据完全是隔离的.
36     每个进程都有一套自己完整的数据,现在使用fork()出来的数据,会把父进程的数据全部拷贝一份到子进程.然后父进程和子进程就完全
37     隔离了.同时这里面的运行过程也是一样的,子进程也会运行一遍,子进程会运行fork()函数之后代码,fork之前的代码子进程是不会运行的
38     所以说子线程也打印了一次jayden,在子线程中pid的值是0,所以说这段代码在子进程中运行就是jayden, 子进程: 24474 , 父进程: 24473
39     如果将print("jayden")语句放到fork()函数上面,按照刚才的理解,jayden就不会打印两次.运行代码输出结果也是如此.
40     
41     如果把time.sleep(2)语句删除执行结果:
42         jayden
43         我是父进程: 24844
44         [root@izhp36e447... ~] # jayden
45         子进程: 24844 , 父进程: 1.
46     通过结果分析:
47         首先父进程打印完成,子进程打印结果并没有接着父进程的打印结果接着打印.是因为父进程运行完就会退出,但是子进程依然可以
48         运行,而且没有退出.我们说是有两个进程,两个进程都是往终端打数据,父进程运行完成并没有sleep,所以说父进程就会直接运行完
49         退出,但是子进程依然存在,而父进程退出,这时子进程就没法退出.
50         
51         使用sleep后,当子进程结束后,父进程还没有结束,这时父进程退出后,就可以将子进程也给kill掉.
52     
53 """

 

multiprocessing进行多进程编程

 1 """
 2 multiprocessing进行多进程编程
 3 
 4 multiprocessing.Process 创建一个进程
 5 """
 6 import multiprocessing
 7 import time
 8 
 9 
10 def get_html(n):
11     time.sleep(n)
12     print("sub progress {} success".format(n))
13     return n
14 
15 
16 if __name__ == '__main__':
17     progress = multiprocessing.Process(target=get_html, args=(2,))
18     progress1 = multiprocessing.Process(target=get_html, args=(3,))
19 
20     # 获取进程的pid(progress id),没有start之前pid是None
21     print(progress.pid)
22     print(progress1.pid)
23 
24     # 开启进程
25     progress.start()
26     progress1.start()
27 
28     # 获取进程的pid
29     print(progress.pid)
30     print(progress1.pid)
31 
32     # 等待所有进程执行完毕再执行主进程
33     progress.join()
34     progress1.join()
35     print("main progress end")
36 
37 """
38 输出结果:
39     None
40     None
41     3356
42     4908
43     sub progress 2 success
44     sub progress 3 success
45     main progress end
46 """

 

 

 1 """
 2 自定义类继承multiprocessing.Process类进行多进程编程
 3 """
 4 import time
 5 import multiprocessing
 6 
 7 
 8 class GetHtmlProgress(multiprocessing.Process):
 9     def __init__(self, n):
10         super().__init__()
11         self.n = n
12 
13     def run(self):
14         time.sleep(self.n)
15         print("sub progress {} success".format(self.n))
16 
17 
18 if __name__ == '__main__':
19     progress1 = GetHtmlProgress(2)
20     progress2 = GetHtmlProgress(3)
21 
22     progress1.start()
23     progress2.start()
24 
25     progress1.join()
26     progress2.join()
27     print("main progress end")
28 
29 """
30 输出结果:
31     sub progress 2 success
32     sub progress 3 success
33     main progress end
34 """

 

 

使用multiprocessing中的进程池 

 1 import time
 2 import multiprocessing
 3 
 4 
 5 def get_html(n):
 6     time.sleep(n)
 7     print("sub progress {} success".format(n))
 8     return n
 9 
10 
11 if __name__ == '__main__':
12     # multiprocessing.Pool(processes=3)
13     # 获取cpu的数量,将cpu的数量作为线程池的数量
14     pool = multiprocessing.Pool(multiprocessing.cpu_count())
15     # 异步提交任务
16     result = pool.apply_async(get_html, args=(3,))
17 
18     # 单独写一行pool.join()会报错,AssertionError:assert self._state in (CLOSE, TERMINATE)
19     # 必须将进程池关闭,不允许进程池再添加新的任务.
20     pool.close()
21 
22     # 将进程池里面所有thread都执行join()方法
23     # 实际上就是等待进程池中的所有任务完成.
24     pool.join()
25 
26     # 返回结果的值
27     print(result.get())
28 
29 """
30 输出结果:
31     sub progress 3 success
32     3
33 """

 

 

 1 """    
 2 imap()函数:
 3     对应executor(线程池)里面的map()方法
 4 
 5 """
 6 import time
 7 import multiprocessing
 8 
 9 
10 def get_html(n):
11     time.sleep(n)
12     print("sub progress {} success".format(n))
13     return n
14 
15 
16 if __name__ == '__main__':
17     # multiprocessing.Pool(processes=3)
18     # 获取cpu的数量,将cpu的数量作为线程池的数量
19     pool = multiprocessing.Pool(multiprocessing.cpu_count())
20 
21     # [1,5,3]秒数列表
22     for result in pool.imap(get_html, [1, 5, 3]):
23         print("{} sleep success".format(result))
24 
25 """
26 输出结果:
27     sub progress 1 success
28     1 sleep success
29     sub progress 3 success
30     sub progress 5 success
31     5 sleep success
32     3 sleep success
33     
34 分析:
35     输出的结果的顺序,与[1,5,3]秒数列表的顺序一致.与线程池中的map()方法是一样的
36 """

 

 

 1 """
 2 imap_unordered()函数:
 3     不按照加载列表的顺序输出结果,按照执行完毕并成功的先后顺序输出结果.
 4 
 5 """
 6 import time
 7 import multiprocessing
 8 
 9 
10 def get_html(n):
11     time.sleep(n)
12     print("sub progress {} success".format(n))
13     return n
14 
15 
16 if __name__ == '__main__':
17     # multiprocessing.Pool(processes=3)
18     # 获取cpu的数量,将cpu的数量作为线程池的数量
19     pool = multiprocessing.Pool(multiprocessing.cpu_count())
20 
21     # [1,5,3]秒数列表
22     for result in pool.imap_unordered(get_html, [1, 5, 3]):
23         print("{} sleep success".format(result))
24 
25 """
26 输出结果:
27     sub progress 1 success
28     1 sleep success
29     sub progress 3 success
30     3 sleep success
31     sub progress 5 success
32     5 sleep success
33     
34 分析:
35     输出的结果的顺序,不按照加载列表的顺序输出结果,按照执行完毕并成功的先后顺序输出结果.
36 """

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

********

posted on 2019-05-03 08:59  jaydenjune  阅读(51)  评论(0)    收藏  举报

导航