1.进程间的通信方式

1.磁盘交互: 速度慢,不安全

2.socket套接字

3.管道通信(Pipe)

4.消息队列(Queue, Manager().Queue, JoinableQueue)

5.共享内存(Value, Array)

6.信号(os.kill, signal)

7.信号量(Semaphore)

8.共享数据(Manager)

2.管道通信-Pipe

1.概述: 在内存中开辟一块空间,对多个进程可见,通过管道实现多进程通信

2.语法

  1. from multiprocess import Pipe
  2.  
  3. fd1, fd2 = Pipe(duplex=True) # duplex默认为True表示双向管道,设置False则表示单向管道
  4. 返回值: 返回两个管道流对象,表示管道的两端
  5. 如果 duplex=True 则是双向管道则 fd1可读可写,fd2可读可写
  6. 如果 duplex=False 则是单向管道则 fd1只能,读fd2只能写
  7. date = fd1.recv() # 接收并返回接收的消息(每次接收一条),如果管道没有消息会阻塞等待
  8. fd2.send(data) # 发送消息,可以是字符串或其他类型,date为要发送的内容,如果没有接收端则管道破裂

3.示例1

  1. from multiprocessing import Process
  2. from multiprocessing import Pipe
  3. import time
  4. import os
  5.  
  6. # 创建一个双向管道
  7. fd1, fd2 = Pipe()
  8.  
  9. # 如果参数为False,则表示创建一个单向管道,此时fd1只等recv,fd2只能send
  10. # fd1, fd2 = Pipe(False)
  11.  
  12. def fun(name):
  13. time.sleep(1)
  14. # 每个子进程都向管道中发送字符串消息
  15. fd1.send("hello %s" % name) # 收发支持Python的多种数据类型,数值,字符串,列表等等
  16. print("fun进程的pid是:%s 父进程的pid是:%s" % (os.getpid(), os.getppid()))
  17.  
  18. jobs = list()
  19. for i in range(5):
  20. p = Process(target=fun, args=(i,))
  21. jobs.append(p)
  22. p.start()
  23.  
  24. # 父进程从管道中取消息
  25. for i in range(5):
  26. data = fd2.recv()
  27. print(data)
  28.  
  29. for i in jobs:
  30. i.join()
  31. """执行结果
  32. fun进程的pid是:24334 父进程的pid是:24333
  33. hello 0
  34. hello 1
  35. fun进程的pid是:24335 父进程的pid是:24333
  36. hello 2
  37. fun进程的pid是:24336 父进程的pid是:24333
  38. hello 3
  39. fun进程的pid是:24337 父进程的pid是:24333
  40. hello 4
  41. fun进程的pid是:24338 父进程的pid是:24333
  42. """

4.示例2

  1. from multiprocessing import Pipe
  2. from multiprocessing import Process
  3.  
  4. def func(con):
  5. con1, con2 = con
  6. con1.close() # 子进程使用con2和父进程通信,所以关闭con1
  7. while 1:
  8. try:
  9. print(con2.recv()) # 当主进程的con1发数据时,子进程要死循环的去接收
  10. except EOFError: # 如果主进程的con1发完数据并关闭con1,子进程的con2继续接收时,就会报错,使用try的方式,获取错误
  11. con2.close() # 获取到错误,就是指子进程已经把管道中所有数据都接收完了,所以用这种方式去关闭管道
  12. break
  13.  
  14. if __name__ == '__main__':
  15. con1, con2 = Pipe()
  16. p = Process(target=func, args=((con1, con2),))
  17. p.start()
  18. con2.close() # 在父进程中,使用con1去和子进程通信,所以不需要con2,就提前关闭
  19. for i in range(10): # 生产数据
  20. con1.send(i) # 给子进程的con2发送数据
  21. con1.close() # 生产完数据,关闭父进程这一端的管道

3.消息队列通信-Queue, Manager().Queue, JoinableQueue

1.消息队列概述
        在内存中开辟队列模型,用来存放消息,任何拥有队列的进程都可以存取消息,消息队列是先进先出

        from queue import Queue  # 是进程内非阻塞队列即线程队列,类似于普通列表

        from multiprocessing import Queue  # 是跨进程通信队列,用于解决多进程间的通信问题

        from multiprocessing import Manager  # 是进程池中各子进程间的通信,使用锁 lock = manager.Queue().Lock()

2.语法

  1. q = Queue(maxsize=0) # 创建一个消息队列,返回消息队列对象
  2. 参数: maxsize默认为0表示队列可存放消息,不指定或数量为负值时容量由内存而定,大于0表示队列最多存放多少条消息
  3. q.put(item,[block[, timeout]]) # 将item消息写入队列,当队列满时会阻塞,要存放的消息(字符串,整数,列表)
  4. 可选参数:
  5. block: 默认为True表示阻塞,这种为False则非阻塞
  6. timeout: blockTrue时设置超时时间,单位是秒, 例如: q.put("test", True, 3)
  7. 如果block值为默认的True
  8. 没有设置timeout,消息列队如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息列队腾出空间为止
  9. 设置了timeout,则会等待timeout秒,若还没空间,则抛出"Queue.Full"异常
  10. 如果block值为False:
  11. 消息列队如果没有空间可写入,则会立刻抛出"Queue.Full"异常
  12. q.get([block[, timeout]]) # 向队列中取出并返回消息,然后将其从列队中移除,当队列空时会阻塞
  13. 可选参数:
  14. block: 默认为True表示阻塞,设置为False则非阻塞
  15. timeout: blockTrue时设置超时时间,单位是秒, 例如 q.get(True, 3)
  16. 如果block值为True
  17. 没有设置timeout,消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止
  18. 设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出"Queue.Empty"异常
  19. 如果block值为False:
  20. 消息列队如果为空,则会立刻抛出"Queue.Empty"异常
  21. q.put_nowait(item) # 用法相当于 q.put(item, False)
  22. q.get_nowait() # 用法相当于 q.get(False)
  23. q.full() # 判断队列是否为满,满则返回True
  24. q.empty() # 判断队列是否为空,空则返回True
  25. q.qsize() # 得到当前队列中消息的个数
  26. q.close() # 关闭队列

3.进程队列

  1. from multiprocessing import Process
  2. from multiprocessing import Queue
  3.  
  4. import time
  5. import random
  6.  
  7. # 写数据进程执行的代码:
  8. def write(q):
  9. for value in ['A', 'B', 'C']:
  10. print('Put %s to queue...' % value)
  11. q.put(value)
  12. time.sleep(random.random())
  13.  
  14. # 读数据进程执行的代码:
  15. def read(q):
  16. while True:
  17. if not q.empty():
  18. value = q.get(True)
  19. print('Get %s from queue.' % value)
  20. time.sleep(random.random())
  21. else:
  22. break
  23.  
  24. if __name__ == '__main__':
  25. # 父进程创建Queue,并传给各个子进程:
  26. q = Queue()
  27. pw = Process(target=write, args=(q,))
  28. pr = Process(target=read, args=(q,))
  29. # 启动子进程pw,写入:
  30. pw.start()
  31. # 等待pw结束:
  32. pw.join()
  33. # 启动子进程pr,读取:
  34. pr.start()
  35. pr.join()
  36. # pr进程里是死循环,无法等待其结束,只能强行终止:
  37. print("")
  38. print("所有数据都写入并且读完")
  39. """执行结果
  40. Put A to queue...
  41. Put B to queue...
  42. Put C to queue...
  43. Get A from queue.
  44. Get B from queue.
  45. Get C from queue.
  46. """

4.进程池队列

  1. # 进程池中使用队列,修改import中的Queue为Manager
  2. from multiprocessing import Manager
  3. from multiprocessing import Pool
  4.  
  5. import os
  6. import time
  7.  
  8. def reader(q):
  9. print("reader启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
  10. for i in range(q.qsize()):
  11. print("reader从Queue获取到消息:%s" % q.get(True))
  12.  
  13. def writer(q):
  14. print("writer启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
  15. for i in "itcast":
  16. q.put(i)
  17.  
  18. if __name__ == "__main__":
  19. print("(%s) start" % os.getpid())
  20. q = Manager().Queue() # 使用Manager中的Queue
  21. po = Pool()
  22. po.apply_async(writer, (q,))
  23.  
  24. time.sleep(1) # 先让上面的任务向Queue存入数据,然后再让下面的任务开始从中取数据
  25.  
  26. po.apply_async(reader, (q,))
  27. po.close()
  28. po.join()
  29. print("(%s) End" % os.getpid())
  30. """执行结果
  31. (24474) start
  32. writer启动(24476),父进程为(24474)
  33. reader启动(24477),父进程为(24474)
  34. reader从Queue获取到消息:i
  35. reader从Queue获取到消息:t
  36. reader从Queue获取到消息:c
  37. reader从Queue获取到消息:a
  38. reader从Queue获取到消息:s
  39. reader从Queue获取到消息:t
  40. (24474) End
  41. """

5.进程队列实现生产者消费者模型

  1. # 实现方案一: 生产者生产结束的标识,放到生产者进程中
  2. from multiprocessing import Queue
  3. from multiprocessing import Process
  4.  
  5. # 生产者
  6. def consumer(q, name):
  7. while 1:
  8. info = q.get()
  9. if info:
  10. print('%s 拿走了%s' % (name, info))
  11. else: # 当消费者获得队列中数据时,如果获得的是None,就是获得到了生产者不再生产数据的标识
  12. break # 此时消费者结束即可
  13.  
  14. # 消费者
  15. def producer(q, product):
  16. for i in range(20):
  17. info = product + '的智能手机%s号' % str(i)
  18. q.put(info)
  19. q.put(None) # 让生产者生产完数据后,给消费者一个不再生产数据的标识
  20.  
  21. if __name__ == '__main__':
  22. q = Queue(10)
  23. p_pro = Process(target=producer, args=(q, '中国制造'))
  24. p_con = Process(target=consumer, args=(q, 'Kali'))
  25. p_pro.start()
  26. p_con.start()
  27. """执行结果
  28. Kali 拿走了中国制造的手办0号
  29. Kali 拿走了中国制造的手办1号
  30. Kali 拿走了中国制造的手办2号
  31. Kali 拿走了中国制造的手办3号
  32. Kali 拿走了中国制造的手办4号
  33. Kali 拿走了中国制造的手办5号
  34. Kali 拿走了中国制造的手办6号
  35. Kali 拿走了中国制造的手办7号
  36. Kali 拿走了中国制造的手办8号
  37. Kali 拿走了中国制造的手办9号
  38. Kali 拿走了中国制造的手办10号
  39. Kali 拿走了中国制造的手办11号
  40. Kali 拿走了中国制造的手办12号
  41. Kali 拿走了中国制造的手办13号
  42. Kali 拿走了中国制造的手办14号
  43. Kali 拿走了中国制造的手办15号
  44. Kali 拿走了中国制造的手办16号
  45. Kali 拿走了中国制造的手办17号
  46. Kali 拿走了中国制造的手办18号
  47. Kali 拿走了中国制造的手办19号
  48. """
  49. # 实现方案二: 生产者生产结束的标识,放到父进程中
  50. from multiprocessing import Queue
  51. from multiprocessing import Process
  52.  
  53. # 生产者
  54. def consumer(q, name, color):
  55. while 1:
  56. info = q.get()
  57. if info:
  58. print('%s%s 拿走了%s \033[0m' % (color, name, info))
  59. else: # 当消费者获得队列中数据时,如果获得的是None,就是获得到了生产者不再生产数据的标识
  60. break # 此时消费者结束即可
  61.  
  62. # 消费者
  63. def producer(q, product):
  64. for i in range(10):
  65. info = product + '的智能手机%s号' % str(i)
  66. q.put(info)
  67.  
  68. if __name__ == '__main__':
  69. q = Queue(10)
  70. # 多个生产者进程
  71. p_pro1 = Process(target=producer, args=(q, '中国制造'))
  72. p_pro2 = Process(target=producer, args=(q, '美国制造'))
  73. p_pro3 = Process(target=producer, args=(q, '日本制造'))
  74. # 多个消费者进程
  75. p_con1 = Process(target=consumer, args=(q, 'Kali', '\033[31m'))
  76. p_con2 = Process(target=consumer, args=(q, 'Coco', '\033[32m'))
  77.  
  78. p_l = [p_con1, p_con2, p_pro1, p_pro2, p_pro3]
  79. [i.start() for i in p_l]
  80.  
  81. # 父进程通过等待生产者进程结束后再发送结束标识
  82. p_pro1.join()
  83. p_pro2.join()
  84. p_pro3.join()
  85. q.put(None) # 几个消费者就要接受几个结束标识
  86. q.put(None)
  87. """执行结果
  88. Kali 拿走了日本制造的智能手机0号
  89. Coco 拿走了日本制造的智能手机1号
  90. Kali 拿走了日本制造的智能手机2号
  91. Coco 拿走了日本制造的智能手机3号
  92. Kali 拿走了日本制造的智能手机4号
  93. Coco 拿走了日本制造的智能手机5号
  94. Kali 拿走了日本制造的智能手机6号
  95. Coco 拿走了日本制造的智能手机7号
  96. Kali 拿走了日本制造的智能手机8号
  97. Coco 拿走了日本制造的智能手机9号
  98. Kali 拿走了美国制造的智能手机0号
  99. Coco 拿走了美国制造的智能手机1号
  100. Kali 拿走了美国制造的智能手机2号
  101. Coco 拿走了美国制造的智能手机3号
  102. Kali 拿走了美国制造的智能手机4号
  103. Coco 拿走了美国制造的智能手机5号
  104. Kali 拿走了美国制造的智能手机6号
  105. Coco 拿走了美国制造的智能手机7号
  106. Kali 拿走了美国制造的智能手机8号
  107. Coco 拿走了美国制造的智能手机9号
  108. Kali 拿走了中国制造的智能手机0号
  109. Coco 拿走了中国制造的智能手机1号
  110. Kali 拿走了中国制造的智能手机2号
  111. Coco 拿走了中国制造的智能手机3号
  112. Kali 拿走了中国制造的智能手机4号
  113. Coco 拿走了中国制造的智能手机5号
  114. Kali 拿走了中国制造的智能手机6号
  115. Coco 拿走了中国制造的智能手机7号
  116. Kali 拿走了中国制造的智能手机8号
  117. Coco 拿走了中国制造的智能手机9号
  118. """

6.进程队列JoinableQueue模块+守护进程实现生产者消费者模型

  1. from multiprocessing import Process,JoinableQueue
  2.  
  3. q = JoinableQueue()
  4.  
  5. # q.join() # 用于生产者,等待 q.task_done的返回结果,通过返回结果,生产者就能获得消费者当前消费了多少个数据
  6. # q.task_done() # 用于消费者,是指每消费队列中一个数据,就给join返回一个标识
  7.  
  8. # 假设生产者生产了100个数据,join就能记录下100这个数字,每次消费者消费一个数据,就必须要task_done返回一个标识
  9. # 当生产者(join)接收到100个消费者返回来的标识的时候,生产者就能知道消费者已经把所有数据都消费完了
  10.  
  11. # 消费者
  12. def consumer(q, name, color):
  13. while 1:
  14. info = q.get()
  15. print('%s %s 拿走了%s \033[0m' % (color, name, info))
  16. q.task_done()
  17.  
  18. # 生产者
  19. def producer(q, product):
  20. for i in range(20):
  21. info = product + '的智能手机%s号' % str(i)
  22. q.put(info)
  23. q.join() # 记录了生产了20个数据在队列中,此时会阻塞等待消费者消费完队列中所有数据
  24.  
  25. if __name__ == '__main__':
  26. q = JoinableQueue(10)
  27. p_pro1 = Process(target=producer, args=(q, '中国制造'))
  28. p_con1 = Process(target=consumer, args=(q, 'Kali', '\033[31m'))
  29. p_con1.daemon = True # 把消费者进程设为守护进程
  30. p_con1.start()
  31. p_pro1.start()
  32. p_pro1.join() # 主进程等待生产者进程结束
  33. # 程序有3个进程,主进程和生产者进程和消费者进程,当主进程执行到p_pro1.join()代码时,主进程会等待生产进程结束
  34. # 而生产进程中执行到q.join()会等待消费者进程把所有数据消费完,生产者进程才结束
  35. # 现在的状态就是主进程等待生产者进程结束,生产者进程等待消费者消费完所有数据
  36. # 所以,把消费者设置为守护进程,当主进程执行完,就代表生产进程已经结束,也就代表消费者进程已经把队列中数据消费完
  37. # 此时,主进程一旦结束,守护进程也就是消费者进程也就跟着结束,整个程序也就能正常结束了
  38. """执行结果
  39. Kali 拿走了中国制造的智能手机0号
  40. Kali 拿走了中国制造的智能手机1号
  41. Kali 拿走了中国制造的智能手机2号
  42. Kali 拿走了中国制造的智能手机3号
  43. Kali 拿走了中国制造的智能手机4号
  44. Kali 拿走了中国制造的智能手机5号
  45. Kali 拿走了中国制造的智能手机6号
  46. Kali 拿走了中国制造的智能手机7号
  47. Kali 拿走了中国制造的智能手机8号
  48. Kali 拿走了中国制造的智能手机9号
  49. Kali 拿走了中国制造的智能手机10号
  50. Kali 拿走了中国制造的智能手机11号
  51. Kali 拿走了中国制造的智能手机12号
  52. Kali 拿走了中国制造的智能手机13号
  53. Kali 拿走了中国制造的智能手机14号
  54. Kali 拿走了中国制造的智能手机15号
  55. Kali 拿走了中国制造的智能手机16号
  56. Kali 拿走了中国制造的智能手机17号
  57. Kali 拿走了中国制造的智能手机18号
  58. Kali 拿走了中国制造的智能手机19号
  59. """

4.共享内存-Value, Array

1.概述: 在内存中开辟一段空间存储数据,对多个进程可见,每次写入共享内存的数据会覆盖之前的内容,对内存格式化较少,但存取速度快

2.语法

  1. from multiprocess import Value
  2. from multiprocess import Array
  3.  
  4. obj = Value(ctype, obj) # 开辟共享内存空间,返回一个共享内存对象
  5. 参数:
  6. ctype: str要转变的c类型(对照ctype表)
  7. obj: 写入共享内存的初始值
  8. obj.value # 得到共享内存中的值
  9. obj = Array(ctype, obj) # 开辟共享内存空间(数组),返回一个共享内存对象
  10. 参数:
  11. ctype: 要转换的类型
  12. obj: 存入到共享内存中的数据,obj是一个列表且列表中的数据类型必须一致,obj是正整数则表示开辟一个多大的内存空间

3.进程间通信-Value

  1. from multiprocessing import Process
  2. from multiprocessing import Value
  3. import time
  4. import random
  5.  
  6. # 向共享内存中存取
  7. def deposit(money):
  8. for i in range(100):
  9. time.sleep(0.01)
  10. money.value += random.randint(1, 100)
  11.  
  12. # 向共享内存中取钱
  13. def withdraw(money):
  14. for i in range(100):
  15. time.sleep(0.01)
  16. money.value -= random.randint(1, 50)
  17.  
  18. def main():
  19. money = Value("i", 1000) # "i"在ctype对照表中对应的数据类型是Python的int类型
  20. de = Process(target=deposit, args=(money,))
  21. wi = Process(target=withdraw, args=(money,))
  22. de.start()
  23. wi.start()
  24. de.join()
  25. wi.join()
  26. print(money.value) # 本次执行结果money.value的值是3401
  27.  
  28. if __name__ == "__main__":
  29. main()

4.进程间通信-Array

  1. from multiprocessing import Process
  2. from multiprocessing import Array
  3.  
  4. def func(shm):
  5. for i in shm:
  6. print(i, end=" ") # 子进程中打印结果: 1 2 3 4 5
  7. shm[0] = 11
  8.  
  9. def main():
  10. # 开辟内存共享空间,可容纳5个整数,初始值是[1, 2, 3, 4, 5]
  11. shm = Array("i", [1, 2, 3, 4, 5]) # "i"在ctype对照表中对应的数据类型是Python的int类型
  12.  
  13. # 子内存共享空间开辟一个包含5个整形的空间
  14. # shm = Array("i", 5) # 没有初始值遍历shm结果为 0 0 0 0 0 此时可以灵活的修改数据
  15.  
  16. p = Process(target=func, args=(shm,))
  17. p.start()
  18. p.join()
  19. print()
  20. for i in shm:
  21. print(i, end=" ") # 主进程中打印结果: 11 2 3 4 5
  22.  
  23. if __name__ == "__main__":
  24. main()

5.管道消息队列共享内存对比

对比项 管道 消息队列 共享内存
开辟空间 内存 内存 内存
读写方式 双向/单向 先进先出  操作覆盖内存 
效率 一般 一般
应用  多用于亲缘进程   方便灵活广泛  较复杂
 是否需要互斥机制 

6.信号-os.kill, signal

1.概述
        1.信号的名字,作用和处理信号都是有系统定义的,一个进程向另一个进程通过信号传递某种讯息

        2.信号的默认处理方法: 系统定义,信号给接收信号的进程带来的行为一般有 终止 暂停 忽略

        3.信号属于异步通信方式,信号的发送不会影响进程的持续执行

        4.命令行下信号操作

            kill -l: 查看信号

            kill -signame PID: 给PID的进程发送一个信号, 例如 kell -9 2332

2.常用信号

  1. # 默认操作: 终止的信号
  2. SIGHUP: 该信号在用户终端连接(正常或非正常)结束时发出,通常是在终端的控制进程结束时,通知同一会话内的各个作业与控制终端不再关系
  3. SIGINT: 该信号在用户键入INTR字符(通常是Ctrl+c)时发出,终端驱动程序发送此信号并送到前台进程中的每一个进程
  4. SIGQUIT: 该信号和SIGINT类似,但由QUIT字符(通常是'"Ctrl+\"')来控制
  5. SIGILL: 该信号在一个进程企图执行一条非法指令时(可执行文件本身出现错误,或者试图执行数据段,堆栈溢出时)发出
  6. SIGFPE: 该信号在发送致命的算术运算错误时发出,不仅包括浮点运算错误还包括溢出和除数为0等其他所有的算数的错误
  7. SIGKILL: 该信号用来立即结束程序的运行,并且不能被阻塞,处理和忽略
  8. SIGALRM: 该信号当一个定时器到时的时候发出
  9. SIGABORT: 该信号用于结束进程
  10.  
  11. # 默认操作: 暂停进程
  12. SIGSTOP: 该信号用于暂停一个进程,并且不能被阻塞,处理和忽略
  13. SIGTSTP: 该信号用于暂停交互进程,用户可以键入SUSP字符(通常是Ctrl+z)发出这个信号
  14.  
  15. # 默认操作: 忽略
  16. SIGCHLD: 子进程改变状态时,父进程会收到这个信号

3.发送信号

语法

  1. import signal
  2.  
  3. os.kill(pid, sig) # 向一个进程发送一个信号
  4. 参数:
  5. pid: 要发送信号的进程PID
  6. sig: 要发送的信号
  7. signal.alarm(sec) # 向自身发送一个时钟信号SIGALRM,一个进程中只能同时有一个时钟,后面的时钟时间会覆盖前面的时钟时间
  8. 参数: sec代表时钟秒数

示例1

  1. import os
  2. import signal
  3.  
  4. # 向 2332 进程发送SIGKILL信号,杀死2332进程
  5. os.kill(2332, signal.SIGKILL)

示例2

  1. import time
  2. import signal
  3.  
  4. # 3秒钟之后向自身发送SIGALRM信号,终止进程
  5. signal.alarm(3)
  6. time.sleep(1)
  7. signal.alarm(5) # 后面的时钟时间会覆盖前面的时钟时间
  8. while True:
  9. time.sleep(1)
  10. print("等待时钟...")

4.信号处理

语法

  1. import signal
  2.  
  3. signal.pause() # 阻塞等待一个信号的发生
  4. signal.signal(signum, handler) # 处理一个信号
  5. 使用说明: 是一个异步处理信号的函数,只要执行在进程中就会按照指定方法处理信号,但是不能处理 SIGSTOP SIGKILL 信号
  6. 参数:
  7. signum: 要处理的信号
  8. handler: 对该信号的处理方法
  9. SIG_DFL: 采用默认方法
  10. SIG_IGN: 忽略这个信号
  11. func: 回调函数,自定义处理方法
  12. 自定义处理方法格式要求
  13. def func(sig, frame): # sig: 接收到的信号;frame: 信号对象
  14. pass

示例1

  1. import time
  2. import signal
  3.  
  4. # 5秒钟之后向自身发送SIGALRM信号,终止进程
  5. signal.alarm(5)
  6.  
  7. # 采用默认的方法处理SIGALRM信号
  8. signal.signal(signal.SIGALRM, signal.SIG_DFL)
  9.  
  10. # 采用忽略信号的方法处理SIGALRM信号
  11. # signal.signal(signal.SIGALRM, signal.SIG_IGN)
  12. # signal.signal(signal.SIGINT, signal.SIG_IGN) # 忽略Ctrl+c终止进程,即进程运行时无法使用Ctrl+c来终止
  13.  
  14. while True:
  15. time.sleep(1)
  16. print("等待时钟...")

示例2

  1. import time
  2. import signal
  3.  
  4. # 5秒钟之后向自身发送SIGALRM信号,终止进程
  5. signal.alarm(5)
  6.  
  7. # 固定格式要求
  8. def handler(sig, frame):
  9. if sig == signal.SIGALRM:
  10. print("收到了时钟信号: %s" % sig)
  11. elif sig == signal.SIGINT:
  12. print("收到了时钟信号: %s" % sig)
  13.  
  14. # 通过自定义方法handler处理SIGALRM信号
  15. signal.signal(signal.SIGALRM, handler)
  16. signal.signal(signal.SIGINT, handler)
  17.  
  18. while True:
  19. time.sleep(1)
  20. print("等待时钟...")

5.使用信号处理僵尸进程
        import signal

原理: 在父进程中忽略子进程的发送信号

        语法: signal.signal(signal.SIGCHLD, signal.SIG_IGN)

6.多进程实现信号通信

  1. import multiprocessing
  2. from signal import *
  3. from time import sleep
  4. import os
  5.  
  6. # 售票员处理信号
  7. def conductor_handler(sig, frame):
  8. if sig == SIGINT:
  9. os.kill(os.getppid(), SIGUSR1)
  10. elif sig == SIGQUIT:
  11. os.kill(os.getppid(), SIGUSR2)
  12. elif sig == SIGUSR1:
  13. print("到站了")
  14. os._exit(0)
  15.  
  16. # 子进程中售票员发送信号
  17. def conductor():
  18. signal(SIGINT, conductor_handler)
  19. signal(SIGQUIT, conductor_handler)
  20. signal(SIGUSR1, conductor_handler)
  21.  
  22. # 子进程中忽略父进程要处理的信号
  23. signal(SIGTSTP, SIG_IGN)
  24. while True:
  25. sleep(3)
  26. print("车内开始广播...")
  27.  
  28. # 司机处理信号
  29. def driver_handler(sig, frame):
  30. if sig == SIGUSR1:
  31. print("老司机开车")
  32. elif sig == SIGUSR2:
  33. print("老司机以锁死车门")
  34. elif sig == SIGTSTP:
  35. os.kill(p_pid, SIGUSR1)
  36.  
  37. def main():
  38. p = multiprocessing.Process(target=conductor)
  39. p.start()
  40. global p_pid
  41. p_pid = p.pid
  42.  
  43. # 父进程中司机发送信号
  44. signal(SIGUSR1, driver_handler)
  45. signal(SIGUSR2, driver_handler)
  46. signal(SIGTSTP, driver_handler)
  47.  
  48. # 父进程忽略子进程要处理的信号
  49. signal(SIGINT, SIG_IGN)
  50. signal(SIGQUIT, SIG_IGN)
  51.  
  52. p.join()
  53.  
  54. if __name__ == "__main__":
  55. main()
  56. """执行结果
  57. ~/Desktop/python3/demo $ python3 demo5.py
  58. 车内开始广播...
  59. 车内开始广播...
  60. ^C老司机开车
  61. ^C老司机开车
  62. ^C老司机开车
  63. 车内开始广播...
  64. ^\老司机以锁死车门
  65. ^\老司机以锁死车门
  66. ^\老司机以锁死车门
  67. 车内开始广播...
  68. 车内开始广播...
  69. ^Z到站了
  70. """

7.信号量-Semaphore

1.概述: 给定一定的信号数量,对多个进程可见并且多个进程均可操作,进程根据信号量的多少可以有不同的行为

2.语法

  1. from multiprocess import Semaphore
  2.  
  3. sem = Semaphore(num) # 定义信号量并返回信号量对象,num是给定信号量的初始个数
  4. sem.acquire() # 将信号量减一,信号量为0时调用次方法会阻塞等待
  5. sem.release() # 将信号量加一

3.示例1

  1. import time
  2. import multiprocessing
  3.  
  4. def func(sem):
  5. print("进程%s等待信号量" % multiprocessing.current_process()) # current_process获取当前进程对象
  6. sem.acquire() # 信号量减1
  7. print("进程%s消耗信号量" % multiprocessing.current_process())
  8. time.sleep(2)
  9. print("进程%s添加信号量" % multiprocessing.current_process())
  10. sem.release() # 信号量加1
  11.  
  12. def main():
  13. # 创建信号量初始值为3
  14. sem = multiprocessing.Semaphore(3)
  15. jobs = list()
  16. for i in range(4):
  17. p = multiprocessing.Process(target=func, args=(sem,))
  18. p.start()
  19. jobs.append(p)
  20. for i in jobs:
  21. i.join()
  22.  
  23. if __name__ == "__main__":
  24. main()
  25. """执行结果
  26. 进程<Process(Process-1, started)>等待信号量
  27. 进程<Process(Process-1, started)>消耗信号量
  28. 进程<Process(Process-2, started)>等待信号量
  29. 进程<Process(Process-2, started)>消耗信号量
  30. 进程<Process(Process-3, started)>等待信号量
  31. 进程<Process(Process-3, started)>消耗信号量
  32. 进程<Process(Process-4, started)>等待信号量
  33. 进程<Process(Process-1, started)>添加信号量
  34. 进程<Process(Process-2, started)>添加信号量
  35. 进程<Process(Process-4, started)>消耗信号量
  36. 进程<Process(Process-3, started)>添加信号量
  37. 进程<Process(Process-4, started)>添加信号量
  38. """

4.示例2

  1. from multiprocessing import Process
  2. from multiprocessing import Semaphore
  3. import time
  4. import random
  5.  
  6. def func(i, sem):
  7. sem.acquire()
  8. print('第%s个人进入小黑屋,拿了钥匙锁上门' % i)
  9. time.sleep(random.randint(3, 5))
  10. print('第%s个人出去小黑屋,还了钥匙打开门' % i)
  11. sem.release()
  12.  
  13. if __name__ == '__main__':
  14. sem = Semaphore(5) # 初始化了一把锁5把钥匙,也就是说允许5个人同时进入小黑屋
  15. # 之后其他人必须等待,等有人从小黑屋出来,还了钥匙,才能允许后边的人进入
  16. for i in range(20):
  17. p = Process(target=func, args=(i, sem,))
  18. p.start()

8.共享数据-Manager

1.概述
        Python中提供了强大的Manager类,专门用于实现多进程之间的数据共享

        Manager类是数据不安全的,Manager类包含的常用方法和属性与Multiprocessing中其他常用类的方法属性一致

        Manager管理的共享数据类型有: Value, Array, dict, list, Lock, Semaphore等等,同时Manager还可以共享类的实例对象

2.Manager实现dict功能

  1. from multiprocessing import Manager, Process, Lock
  2.  
  3. def work(d, lock):
  4. with lock: # 不加锁而操作共享的数据,肯定会出现数据错乱
  5. d['count'] -= 1
  6.  
  7. if __name__ == '__main__':
  8. lock = Lock()
  9. with Manager() as m:
  10. dic = m.dict({'count': 100})
  11. p_l = []
  12. for i in range(100):
  13. p = Process(target=work, args=(dic, lock))
  14. p_l.append(p)
  15. p.start()
  16. for p in p_l:
  17. p.join()
  18. print(dic)

3.Manager实现list功能

  1. from multiprocessing import Manager, Process
  2.  
  3. def func(num):
  4. num[0] -= 1
  5. print("子进程中num的值是%s" % num)
  6.  
  7. if __name__ == "__main__":
  8. m = Manager()
  9. num = m.list([1, 2, 3])
  10. p = Process(target=func, args=(num,))
  11. p.start()
  12. p.join()
  13. print("父进程中num的值是%s" % num)

4.通过Manager进程间共享实例对象

  1. from multiprocessing import Process, Value, Lock
  2. from multiprocessing.managers import BaseManager
  3.  
  4. class Employee(object):
  5. def __init__(self, name, salary):
  6. self.name = name
  7. self.salary = Value('i', salary)
  8.  
  9. def increase(self):
  10. self.salary.value += 100
  11.  
  12. def getPay(self):
  13. return self.name + ':' + str(self.salary.value)
  14.  
  15. class MyManager(BaseManager):
  16. pass
  17.  
  18. def Manager2():
  19. m = MyManager()
  20. m.start()
  21. return m
  22.  
  23. MyManager.register('Employee', Employee)
  24.  
  25. def func1(em, lock):
  26. with lock:
  27. em.increase()
  28.  
  29. if __name__ == '__main__':
  30. manager = Manager2()
  31. em = manager.Employee('zhangsan', 1000)
  32. lock = Lock()
  33. proces = [Process(target=func1, args=(em, lock)) for i in range(10)]
  34. for p in proces:
  35. p.start()
  36. for p in proces:
  37. p.join()
  38. print(em.getPay())

05_进程间通信 IPC的更多相关文章

  1. Linux进程间通信IPC学习笔记之同步二(SVR4 信号量)

    Linux进程间通信IPC学习笔记之同步二(SVR4 信号量)

  2. Linux进程间通信IPC学习笔记之同步二(Posix 信号量)

    Linux进程间通信IPC学习笔记之同步二(Posix 信号量)

  3. Linux进程间通信IPC学习笔记之消息队列(SVR4)

    Linux进程间通信IPC学习笔记之消息队列(SVR4)

  4. Android进程间通信IPC

    一.IPC的说明 IPC是Inter-Process Communication的缩写,含义为进程间通信或跨进程通信,是指两个进程之间进行数据交换的过程. IPC不是Android独有的,任何一个操作 ...

  5. 进程间通信IPC -- 管道, 队列

    进程间通信--IPC(Inter-Process Communication) 管道 from multiprocessing import Pipecon1,con2 = Pipe()管道是不安全的 ...

  6. [原创]chromium源码阅读-进程间通信IPC.消息的接收与应答

    chromium源码阅读-进程间通信IPC.消息的接收与应答   chromium源码阅读-进程间通信IPC.消息的接收与应答 介绍 chromium进程间通信在win32下是通过命名管道的方式实现的 ...

  7. 进程间通信IPC之--无名管道(pipe)和有名管道(fifo)(转)

     进程间通信IPC之--无名管道(pipe)和有名管道(fifo) 2012-01-17 22:41:20 分类: C/C++ 每个进程各自有不同的用户地址空间,任何一个进 程的全局变量在另一个进程中 ...

  8. 进程间通信IPC、LPC、RPC

    进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法.进程是计算机系统分配资源的最小单位.每个进程都有自己的一部分独立的系 ...

  9. 【Android】进程间通信IPC——Binder

    Binder是Android中的跨进程通信方式,bindService的时候,服务端返回Binder对象,通过该对象客户端可以从服务端获取数据.在进程间通信IPC——AIDL中创建了ICustomAi ...

随机推荐

  1. day25:魔术方法

    目录 1.__del__(析构方法) 2.魔术方法:__str__ 3.魔术方法:__repr__ 4.魔术方法:__call__ 5.魔术方法:__bool__ 6.魔术方法:__add__& ...

  2. Linux文本处理详细教程

    1. 文本处理 本节将介绍Linux下使用Shell处理文本时最常用的工具: find.grep.xargs.sort.uniq.tr.cut.paste.wc.sed.awk: 提供的例子和参数都是 ...

  3. XCTF-WEB-高手进阶区(1-4)笔记

    1:baby_web 题目描述:想想初始页面是哪个 通过Dirsearch软件扫描发现Index.php被藏起来了,访问他便会指向1.php 于是通过Burp修改Get为index.php,然后放入R ...

  4. java 多态一

    一 多态的概述 现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学 生也是人,即出现两种形态. Java作为面向对象的语言,同样可以描述一个事物的多种形态.如Studen ...

  5. C#LeetCode刷题之#896-单调数列(Monotonic Array)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3760 访问. 如果数组是单调递增或单调递减的,那么它是单调的. ...

  6. TypeScript是什么,为什么要使用它?

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文出处:https://medium.com/swlh/what-is-typescript-bf333e ...

  7. DUBBO学习心得

     项目环境版本:dubbo2.5.10 spring版本4.3.10  一 SOA 1英文名称(Service Oriented Ambiguity) 2 中文名称:面向服务架构 2.1 有一个专门提 ...

  8. ybt1107题解和方法总结

    今天花了三个小时的时间刷了些基础题,虽说是简单题,但是有一些还是有点难度的 比如ybt1107,我死嗑了半个小时. [题目描述] 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米. ...

  9. String、StringBuilder、StringBuffer三者的区别

    StringBuffer.StringBuilder和String都可以用来代表字符串.String类是不可变类,任何对String的改变都会引发新的String对象的生成:StringBuffer. ...

  10. 【API进阶之路】逆袭!用关键词抽取API搞定用户需求洞察

    摘要: 老大说,我这份用关键词抽取API搞定的用户需求洞察报告,简直比比市场调研的科班人士做得还好. 最近这半个月的午饭,那可是相当不错,市场老大天天请吃饭,不是外面下馆子,就是从家带饺子.说是感谢我 ...