多进程

进程之间是相互独立的,python是启动进程的时候,是启动的是原生进程。进程是没有GIL锁的,而且不存在锁的概念,进程之间的数据式不能共享的,而线程是可以的。

1、进程的定义

用muliprocessing这个包中的Process来定义多进程,跟定义多线程类似

  1. from multiprocessing import Process # 导入进程模块
  2. import time
  3.  
  4. def run(name):
  5. time.sleep(2)
  6. print("hello", name)
  7.  
  8. if __name__ == "__main__":
  9. p_obj_list = list() # 存放进程对象
  10. for i in range(10): # 启动10个进程
  11. p = Process(target=run, args=("QQ{0}".format(i),)) # 产生一个进程实例
  12. p.start() # 启动进程
  13. p_obj_list.append(p)
  14.  
  15. for p in p_obj_list:
  16. p.join() # 等待进程结果

2、进程中加入线程

  1. from multiprocessing import Process
  2. import time,threading
  3.  
  4. def thread_run(name): # 定义线程执行的方法
  5. print("{0}:{1}".format(name, threading.get_ident())) # thread.get_ident ()返回当前线程的标识符,标识符是一个非零整数
  6.  
  7. def run(name):
  8. time.sleep(2)
  9. print("hello", name)
  10. t = threading.Thread(target=thread_run, args=(name,)) # 嵌入线程
  11. t.start() # 执行线程
  12.  
  13. if __name__ == "__main__":
  14. p_obj_list = list()
  15. for i in range(10):
  16. p = Process(target=run, args=("QQ{0}".format(i),))
  17. p.start()
  18. p_obj_list.append(p)
  19.  
  20. for p in p_obj_list:
  21. p.join()

3、父子进程

每个子进程都是由一个父进程启动的,每个程序也是有一个父进程

  1. from multiprocessing import Process
  2. import os
  3.  
  4. def info(title):
  5. print(title)
  6. print('module name:', __name__)
  7. print('parent process:', os.getppid()) # 获得父进程ID
  8. print('process id:', os.getpid()) # 获得子进程ID
  9. print("\n\n")
  10.  
  11. def f(name):
  12. info('\033[31;1m function f\033[0m')
  13. print('hello', name)
  14.  
  15. if __name__ == '__main__':
  16. info('\033[32;1m main process line\033[0m')
  17. p = Process(target=f, args=('QQ',))
  18. p.start()
  19. p.join()

  

进程间数据交互与共享

知道不同进程之间内存是不共享的,要想实现两个进程间的通信需要用到multiprocessing库中的queue(队列)模块,这个multiprocessing库中的queue模块跟单纯的queue库是不一样的。进程导入前者(这里的queue是专门为进程之间的通信设计的)不出错,导入后者(这里的queue主要是线程间数据交互)出错。

1、线程访问queue

  1. import queue,threading
  2.  
  3. def f(q):
  4. q.put([66, None, 'hello word'])
  5.  
  6. if __name__ == '__main__':
  7. q = queue.Queue() # 把这个q传给了子线程
  8. p = threading.Thread(target=f, args=(q,)) # 子线程访问父线程的q
  9. p.start()
  10. print(q.get())
  11. p.join()
  12.  
  13. #执行结果
  14. [66, None, 'hello word']

2、进程访问queue

  1. from multiprocessing import Process
  2. import queue
  3.  
  4. def f(q):
  5. q.put([66, None, 'hello word'])
  6.  
  7. if __name__ == '__main__':
  8. q = queue.Queue() # 把这个q传给了子线程
  9. p = Process(target=f, args=(q,)) # 子线程访问父线程的q
  10. p.start()
  11. print(q.get())
  12. p.join()
  13.  
  14. #执行结果
  15. Traceback (most recent call last):
  16. File "C:/Users/dell/PycharmProjects/untitled/process/进程的定义.py", line 77, in <module>
  17. p.start()
  18. File "C:\Python36\lib\multiprocessing\process.py", line 105, in start
  19. self._popen = self._Popen(self)
  20. File "C:\Python36\lib\multiprocessing\context.py", line 223, in _Popen
  21. return _default_context.get_context().Process._Popen(process_obj)
  22. File "C:\Python36\lib\multiprocessing\context.py", line 322, in _Popen
  23. return Popen(process_obj)
  24. File "C:\Python36\lib\multiprocessing\popen_spawn_win32.py", line 65, in __init__
  25. reduction.dump(process_obj, to_child)
  26. File "C:\Python36\lib\multiprocessing\reduction.py", line 60, in dump
  27. ForkingPickler(file, protocol).dump(obj)
  28. TypeError: can't pickle _thread.lock objects

3、进程访问multiprocessing库中的Queue模块

  1. from multiprocessing import Process,Queue
  2.  
  3. def f(q):
  4. q.put([66, None, 'hello word'])
  5.  
  6. if __name__ == '__main__':
  7. q = Queue() # 把这个q传给了子线程
  8. p = Process(target=f, args=(q,)) # 子线程访问父线程的q
  9. p.start()
  10. print(q.get())
  11. p.join()
  12.  
  13. #执行结果
  14. [66, None, 'hello word']

父进程相当于克隆一个Q,把自己的Q克隆了一份交给子进程,子进程这个时候往Q里面放了一份数据,然后父进程又能实际的获取到。但是你克隆了一份是不是就和父进程没有关系了,为什么还能联系在一起呢?但是实际上:等于这两个Q里面的数据又把它序列化了,序列化到一个中间的地方,类似于翻译,然后反序列化给这个父进程这边来了,其实这两个Q就是通过pickle来序列化的,不是一个真正的Q。

小结:两个线程之间可以修改一个数据,不加锁,可能就会出错。现在进程中的Queue,是实现了数据的传递,不是在修改同一份数据,只是实现一个进程的数据传给了另外一个进程。

4、通过Pipe()实现进程间的数据交互,manger实现数据共享

上面的例子是通过进程中的Queue,来进行数据共享的,其实还有一种方式实现数据共享,那就是管道,pipe,以及数据共享manger。

4.1、Pipe()函数

管道函数会返回由管道双方连接的一组连接对象,该管道默认是双向的(双向的)。

  1. from multiprocessing import Process, Pipe
  2.  
  3. def f(conn):
  4. conn.send([66, None, 'hello,word']) # 发送消息给父进程
  5. conn.close()
  6.  
  7. if __name__ == '__main__':
  8. parent_conn, child_conn = Pipe() # 管道生成返回两个实例,是双向的,这边把第1个作为父连接,第2个作为子连接。也可以,两者角色调换一下
  9. p = Process(target=f, args=(child_conn,))
  10. p.start()
  11. print(parent_conn.recv()) # 接收子进程的消息
  12. p.join()

4.2、接受多次和发送多次

  1. from multiprocessing import Process, Pipe
  2.  
  3. def f(conn):
  4. conn.send([66, None, 'hello,word']) # 发送消息给父进程
  5. conn.send("QQ") # 发送消息给父进程
  6. print(conn.recv()) # 接收父进程的消息
  7. conn.close()
  8.  
  9. if __name__ == '__main__':
  10. parent_conn, child_conn = Pipe() # 管道生成返回两个实例,是双向的,这边把第1个作为父连接,第2个作为子连接。也可以,两者角色调换一下
  11. p = Process(target=f, args=(child_conn,))
  12. p.start()
  13. print(parent_conn.recv())
  14. print(parent_conn.recv()) # 接收两次
  15. parent_conn.send("微信") # 发送给子进程
  16. p.join()

4.3、manger

manger可以完成数据间的共享。

  1. from multiprocessing import Process, Manager
  2. import os
  3.  
  4. def f(d, l):
  5. d[os.getpid()] = os.getpid()
  6. l.append(os.getpid())
  7. print(l)
  8.  
  9. if __name__ == '__main__':
  10. with Manager() as manager:
  11. d = manager.dict() # 声明一个字典,这个字典是用manger声明的,不是用dict()声明的
  12. # manger.dict()是用专门的语法生产一个可在多进程之间进行传递和共享的一个字典
  13. l = manager.list(range(5)) # 同样声明一个列表
  14. p_list = []
  15. for i in range(10):
  16. p = Process(target=f, args=(d, l))
  17. p.start()
  18. p_list.append(p)
  19. for res in p_list:
  20. res.join()
  21. print(d)
  22. print(l)

线程修改同一份数据的时候需要加锁,进程修改数据呢:不用加锁,因为这个manger已经帮你加锁了,它就默认不允许两个进程同时修改一份数据。两个进程没有办法同时修改一份数据,进程之间是独立的,它自己也要加锁,因为它把自己的东西同时copy好几份,跟刚刚的那个Queue一样,copy10个字典最终合成一个字典

进程锁和进程池的使用

1、进程锁

通过multiprocessing中的Lock模块来实现进程锁

  1. from multiprocessing import Process,Lock # 导入进程锁
  2.  
  3. def f(l, i):
  4. l.acquire() # 加锁
  5. try:
  6. print("hello word", i)
  7. finally:
  8. l.release() # 释放锁
  9.  
  10. if __name__ == "__main__":
  11. lock = Lock() # 定义锁
  12. for num in range(10):
  13. Process(target=f, args=(lock, num,)).start() # 把锁传入进程中

进程中不是相互独立的吗?为什么还要加锁:虽然每个进程都是独立运行的,但是问题来了,它们共享一块屏幕。这个锁存在的意义就是屏幕共享。如果进程1想着打印数据,而进程2想也想打印数据的情况,就有可能乱套了,然后通过这个锁来控制,去打印的时候,这个屏幕只有我独占,导致屏幕不会乱。

2、进程池apply和apply_saync

2.1、appley

同步执行,也就是串行执行的

  1. from multiprocessing import Pool # 导入进程池模块pool
  2. import time,os
  3.  
  4. def foo(i):
  5. time.sleep(2)
  6. print("in process", os.getpid()) # 打印进程号
  7.  
  8. if __name__ == "__main__":
  9. pool = Pool(processes=5) # 设置进程池个数为5,也可以写成pool = Pool(5),允许进程池同时放入5个进程,并且这5个进程交给cpu去运行
  10. for i in range(10):
  11. pool.apply(func=foo, args=(i,)) # 同步执行挂起进程
  12. print('end')
  13. pool.close()
  14. pool.join() # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。

2.2、apply_saync

异步执行,也就是并行执行。

  1. from multiprocessing import Pool # 导入进程池模块pool
  2. import time,os
  3.  
  4. def foo(i):
  5. time.sleep(2)
  6. print("in process", os.getpid()) # 打印进程号
  7.  
  8. if __name__ == "__main__":
  9. pool = Pool(processes=5) # 设置进程池个数为5,也可以写成pool = Pool(5),允许进程池同时放入5个进程,并且这5个进程交给cpu去运行
  10. for i in range(10):
  11. pool.apply_async(func=foo, args=(i,)) # 采用异步方式执行foo函数
  12. print('end')
  13. pool.close()
  14. pool.join() # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。

2.3、异步下回调函数

程序执行完毕之后,再回调过来执行这个Bar函数。

  1. from multiprocessing import Process,Pool
  2. import time,os
  3.  
  4. def foo(i):
  5. time.sleep(2)
  6. print("in process", os.getpid()) # 打印子进程的进程号
  7.  
  8. def bar(arg):
  9. print('-->exec done:', arg, os.getpid()) # 打印进程号
  10.  
  11. if __name__ == "__main__":
  12. pool = Pool(processes=2)
  13. print("主进程", os.getpid()) # 主进程的进程号
  14. for i in range(3):
  15. pool.apply_async(func=foo, args=(i,), callback=bar) # 执行回调函数callback=Bar
  16. print('end')
  17. pool.close()
  18. pool.join() # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。
  19.  
  20. #执行结果
  21. 主进程 752
  22. end
  23. in process 2348
  24. -->exec done: None 752
  25. in process 8364
  26. -->exec done: None 752
  27. in process 2348
  28. -->exec done: None 752

注:

  1. 回调函数说明fun=Foo干不完就不执行bar函数,等Foo执行完就去执行Bar
  2. 这个回调函数是主进程去调用的,而不是每个子进程去调用的。
  3. 回调函数的用处:

      比如说你从各个机器上备份完毕,在回调函数中自动写一个脚本,说备份完毕

  4. 回调函数是主进程调用的原因?

      如果是子进程去调用这个回调函数,有多少个子进程就有多少个连接,如果是主进程的话,只需要一次长连接就可以了,这个效率就高了

  

【python】-- 多进程的基本语法 、进程间数据交互与共享、进程锁和进程池的使用的更多相关文章

  1. Python 进程间数据交互

    进程间通信:进程之间必须需要中间件. 不同进程间内存是不共享的,要想实现两个进程间的数据交换     Queues:实现传输两个进程的数据 线程queue,访问数据只能在一个进程内进行线程与线程之间的 ...

  2. [转]WINDOW进程间数据通讯以及共享内存

    1.引言 在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯.WIN32 API提供了许多函数使我们能够方便高效地进行进程间的通讯,通过这些函数我们可以控制不同进程间的数据交换,就如同 ...

  3. Python多进程-进程间数据的传递

    两个进程间的数据是独立的,要进行数据传递的话可通过几个方法 Queue 通过队列来进行进程间数据的传递 # -*- coding:utf-8 -*- __author__ = "MuT6 S ...

  4. Python进阶----进程间数据隔离, join阻塞等待, 进程属性, 僵尸进程和孤儿进程, 守护进程

    Python进阶----进程间数据隔离, join阻塞等待, 进程属性, 僵尸进程和孤儿进程, 守护进程 一丶获取进程以及父进程的pid 含义:    进程在内存中开启多个,操作系统如何区分这些进程, ...

  5. 子进程回收资源两种方式,僵尸进程与孤儿进程,守护进程,进程间数据隔离,进程互斥锁,队列,IPC机制,线程,守护线程,线程池,回调函数add_done_callback,TCP服务端实现并发

    子进程回收资源两种方式 - 1) join让主进程等待子进程结束,并回收子进程资源,主进程再结束并回收资源. - 2) 主进程 “正常结束” ,子进程与主进程一并被回收资源. from multipr ...

  6. python之 《进程之间数据交互和进程池》

    1.进程q 进程呢就相当于一个房子,线程就相当于是房子里面在工作的人,那么一个房子的空间对于房子里面的人来说是共享的, 现在是多进程,也就是说有许多房子,很显然这个房子的空间只属于这个房子,不会属于其 ...

  7. 04 . Vue组件注册,组件间数据交互,调试工具及组件插槽介绍及使用

    vue组件 组件(Component)是 Vue.js 最强大的功能之一. 组件可以扩展 HTML 元素,封装可重用的代码. 组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的 ...

  8. python 使用多进程实现并发编程/使用queue进行进程间数据交换

    import time import os import multiprocessing from multiprocessing import Queue, pool ""&qu ...

  9. Python多进程-进程间数据的共享

    不同的进程不能同时修改一份数据,但是不同的进程能对一份数据进行修改 可通过Manager来实现进程间的数据共享 # -*- coding:utf-8 -*- __author__ = "Mu ...

随机推荐

  1. linux /boot目录下的文件分析

    一. Linux 启动流程 首先说一下Linux系统大概的启动过程: 1. 主机加电后, 系统首先加载BIOS, 这个BIOS是以写在主板上的. 2. BIOS启动后,执行一些例如开机自检,硬件初始化 ...

  2. 转: javascript技术栈

    http://www.infoq.com/cn/articles/state-of-javascript-2016

  3. IDEA如何打包可运行jar的一个问题

    转载:http://bglmmz.iteye.com/blog/2058785 背景: 有时候,我们会用IDEA来开发一些小工具,需要打成可运行的JAR包:或者某些项目不是WEB应用,纯粹是后台应用, ...

  4. shell3

    例一:监控httpd是否正常运行并自动开启 #!/bin/bash port=$(nmap -sT localhost | grep 'http$' |awk '{print $2}') riqi=$ ...

  5. 给mysql root用户设置密码

    使用其他用户进入数据库, 用select PASSWORD('你要设置的密码'), 然后直接update mysql.user set  mysql.user.Password='你PASSWORD( ...

  6. 结合jquery的前后端加密解密 适用于WebApi的SQL注入过滤器 Web.config中customErrors异常信息配置 ife2018 零基础学院 day 4 ife2018 零基础学院 day 3 ife 零基础学院 day 2 ife 零基础学院 day 1 - 我为什么想学前端

    在一个正常的项目中,登录注册的密码是密文传输到后台服务端的,也就是说,首先前端js对密码做处理,随后再传递到服务端,服务端解密再加密传出到数据库里面.Dotnet已经提供了RSA算法的加解密类库,我们 ...

  7. Python---copy()、deepcopy()与赋值的区别

    copy()与deepcopy()之间的主要区别是python对数据的存储方式. 首先直接上结论: —–深复制,即将被复制对象完全再复制一遍作为独立的新个体单独存在.所以改变原有被复制对象不会对已经复 ...

  8. 怎样封装RESTful Web Service

    所谓Web Service是一个平台独立的,低耦合的.自包括的.可编程的Web应用程序.有了Web Service异构系统之间就能够通过XML或JSON来交换数据,这样就能够用于开发分布式的互操作的应 ...

  9. android studio改动module名称

    新建一个android studio项目,默认Module名称是app 右键app选择Rename,或者Shift + F6也能够.重命名module名称 重命名为abc之后中,如图上面箭头所指的ap ...

  10. MySQL 用户与权限管理

    MySQL权限系统的主要功能是证实连接到一台给定主机的用户,而且赋予该用户在数据库上的相关DML,DQL权限.MySQL存取控制包括2个阶段,一是server检查是否同意你连接:二是假定你能连接,se ...