05_进程间通信 IPC
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.语法
- from multiprocess import Pipe
- fd1, fd2 = Pipe(duplex=True) # duplex默认为True表示双向管道,设置False则表示单向管道
- 返回值: 返回两个管道流对象,表示管道的两端
- 如果 duplex=True 则是双向管道则 fd1可读可写,fd2可读可写
- 如果 duplex=False 则是单向管道则 fd1只能,读fd2只能写
- date = fd1.recv() # 接收并返回接收的消息(每次接收一条),如果管道没有消息会阻塞等待
- fd2.send(data) # 发送消息,可以是字符串或其他类型,date为要发送的内容,如果没有接收端则管道破裂
3.示例1
- from multiprocessing import Process
- from multiprocessing import Pipe
- import time
- import os
- # 创建一个双向管道
- fd1, fd2 = Pipe()
- # 如果参数为False,则表示创建一个单向管道,此时fd1只等recv,fd2只能send
- # fd1, fd2 = Pipe(False)
- def fun(name):
- time.sleep(1)
- # 每个子进程都向管道中发送字符串消息
- fd1.send("hello %s" % name) # 收发支持Python的多种数据类型,数值,字符串,列表等等
- print("fun进程的pid是:%s 父进程的pid是:%s" % (os.getpid(), os.getppid()))
- jobs = list()
- for i in range(5):
- p = Process(target=fun, args=(i,))
- jobs.append(p)
- p.start()
- # 父进程从管道中取消息
- for i in range(5):
- data = fd2.recv()
- print(data)
- for i in jobs:
- i.join()
- """执行结果
- fun进程的pid是:24334 父进程的pid是:24333
- hello 0
- hello 1
- fun进程的pid是:24335 父进程的pid是:24333
- hello 2
- fun进程的pid是:24336 父进程的pid是:24333
- hello 3
- fun进程的pid是:24337 父进程的pid是:24333
- hello 4
- fun进程的pid是:24338 父进程的pid是:24333
- """
4.示例2
- from multiprocessing import Pipe
- from multiprocessing import Process
- def func(con):
- con1, con2 = con
- con1.close() # 子进程使用con2和父进程通信,所以关闭con1
- while 1:
- try:
- print(con2.recv()) # 当主进程的con1发数据时,子进程要死循环的去接收
- except EOFError: # 如果主进程的con1发完数据并关闭con1,子进程的con2继续接收时,就会报错,使用try的方式,获取错误
- con2.close() # 获取到错误,就是指子进程已经把管道中所有数据都接收完了,所以用这种方式去关闭管道
- break
- if __name__ == '__main__':
- con1, con2 = Pipe()
- p = Process(target=func, args=((con1, con2),))
- p.start()
- con2.close() # 在父进程中,使用con1去和子进程通信,所以不需要con2,就提前关闭
- for i in range(10): # 生产数据
- con1.send(i) # 给子进程的con2发送数据
- 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.语法
- q = Queue(maxsize=0) # 创建一个消息队列,返回消息队列对象
- 参数: maxsize默认为0表示队列可存放消息,不指定或数量为负值时容量由内存而定,大于0表示队列最多存放多少条消息
- q.put(item,[block[, timeout]]) # 将item消息写入队列,当队列满时会阻塞,要存放的消息(字符串,整数,列表)
- 可选参数:
- block: 默认为True表示阻塞,这种为False则非阻塞
- timeout: 在block为True时设置超时时间,单位是秒, 例如: q.put("test", True, 3)
- 如果block值为默认的True
- 没有设置timeout,消息列队如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息列队腾出空间为止
- 设置了timeout,则会等待timeout秒,若还没空间,则抛出"Queue.Full"异常
- 如果block值为False:
- 消息列队如果没有空间可写入,则会立刻抛出"Queue.Full"异常
- q.get([block[, timeout]]) # 向队列中取出并返回消息,然后将其从列队中移除,当队列空时会阻塞
- 可选参数:
- block: 默认为True表示阻塞,设置为False则非阻塞
- timeout: 在block为True时设置超时时间,单位是秒, 例如 q.get(True, 3)
- 如果block值为True
- 没有设置timeout,消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止
- 设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出"Queue.Empty"异常
- 如果block值为False:
- 消息列队如果为空,则会立刻抛出"Queue.Empty"异常
- q.put_nowait(item) # 用法相当于 q.put(item, False)
- q.get_nowait() # 用法相当于 q.get(False)
- q.full() # 判断队列是否为满,满则返回True
- q.empty() # 判断队列是否为空,空则返回True
- q.qsize() # 得到当前队列中消息的个数
- q.close() # 关闭队列
3.进程队列
- from multiprocessing import Process
- from multiprocessing import Queue
- import time
- import random
- # 写数据进程执行的代码:
- def write(q):
- for value in ['A', 'B', 'C']:
- print('Put %s to queue...' % value)
- q.put(value)
- time.sleep(random.random())
- # 读数据进程执行的代码:
- def read(q):
- while True:
- if not q.empty():
- value = q.get(True)
- print('Get %s from queue.' % value)
- time.sleep(random.random())
- else:
- break
- if __name__ == '__main__':
- # 父进程创建Queue,并传给各个子进程:
- q = Queue()
- pw = Process(target=write, args=(q,))
- pr = Process(target=read, args=(q,))
- # 启动子进程pw,写入:
- pw.start()
- # 等待pw结束:
- pw.join()
- # 启动子进程pr,读取:
- pr.start()
- pr.join()
- # pr进程里是死循环,无法等待其结束,只能强行终止:
- print("")
- print("所有数据都写入并且读完")
- """执行结果
- Put A to queue...
- Put B to queue...
- Put C to queue...
- Get A from queue.
- Get B from queue.
- Get C from queue.
- """
4.进程池队列
- # 进程池中使用队列,修改import中的Queue为Manager
- from multiprocessing import Manager
- from multiprocessing import Pool
- import os
- import time
- def reader(q):
- print("reader启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
- for i in range(q.qsize()):
- print("reader从Queue获取到消息:%s" % q.get(True))
- def writer(q):
- print("writer启动(%s),父进程为(%s)" % (os.getpid(), os.getppid()))
- for i in "itcast":
- q.put(i)
- if __name__ == "__main__":
- print("(%s) start" % os.getpid())
- q = Manager().Queue() # 使用Manager中的Queue
- po = Pool()
- po.apply_async(writer, (q,))
- time.sleep(1) # 先让上面的任务向Queue存入数据,然后再让下面的任务开始从中取数据
- po.apply_async(reader, (q,))
- po.close()
- po.join()
- print("(%s) End" % os.getpid())
- """执行结果
- (24474) start
- writer启动(24476),父进程为(24474)
- reader启动(24477),父进程为(24474)
- reader从Queue获取到消息:i
- reader从Queue获取到消息:t
- reader从Queue获取到消息:c
- reader从Queue获取到消息:a
- reader从Queue获取到消息:s
- reader从Queue获取到消息:t
- (24474) End
- """
5.进程队列实现生产者消费者模型
- # 实现方案一: 生产者生产结束的标识,放到生产者进程中
- from multiprocessing import Queue
- from multiprocessing import Process
- # 生产者
- def consumer(q, name):
- while 1:
- info = q.get()
- if info:
- print('%s 拿走了%s' % (name, info))
- else: # 当消费者获得队列中数据时,如果获得的是None,就是获得到了生产者不再生产数据的标识
- break # 此时消费者结束即可
- # 消费者
- def producer(q, product):
- for i in range(20):
- info = product + '的智能手机%s号' % str(i)
- q.put(info)
- q.put(None) # 让生产者生产完数据后,给消费者一个不再生产数据的标识
- if __name__ == '__main__':
- q = Queue(10)
- p_pro = Process(target=producer, args=(q, '中国制造'))
- p_con = Process(target=consumer, args=(q, 'Kali'))
- p_pro.start()
- p_con.start()
- """执行结果
- Kali 拿走了中国制造的手办0号
- Kali 拿走了中国制造的手办1号
- Kali 拿走了中国制造的手办2号
- Kali 拿走了中国制造的手办3号
- Kali 拿走了中国制造的手办4号
- Kali 拿走了中国制造的手办5号
- Kali 拿走了中国制造的手办6号
- Kali 拿走了中国制造的手办7号
- Kali 拿走了中国制造的手办8号
- Kali 拿走了中国制造的手办9号
- Kali 拿走了中国制造的手办10号
- Kali 拿走了中国制造的手办11号
- Kali 拿走了中国制造的手办12号
- Kali 拿走了中国制造的手办13号
- Kali 拿走了中国制造的手办14号
- Kali 拿走了中国制造的手办15号
- Kali 拿走了中国制造的手办16号
- Kali 拿走了中国制造的手办17号
- Kali 拿走了中国制造的手办18号
- Kali 拿走了中国制造的手办19号
- """
- # 实现方案二: 生产者生产结束的标识,放到父进程中
- from multiprocessing import Queue
- from multiprocessing import Process
- # 生产者
- def consumer(q, name, color):
- while 1:
- info = q.get()
- if info:
- print('%s%s 拿走了%s \033[0m' % (color, name, info))
- else: # 当消费者获得队列中数据时,如果获得的是None,就是获得到了生产者不再生产数据的标识
- break # 此时消费者结束即可
- # 消费者
- def producer(q, product):
- for i in range(10):
- info = product + '的智能手机%s号' % str(i)
- q.put(info)
- if __name__ == '__main__':
- q = Queue(10)
- # 多个生产者进程
- p_pro1 = Process(target=producer, args=(q, '中国制造'))
- p_pro2 = Process(target=producer, args=(q, '美国制造'))
- p_pro3 = Process(target=producer, args=(q, '日本制造'))
- # 多个消费者进程
- p_con1 = Process(target=consumer, args=(q, 'Kali', '\033[31m'))
- p_con2 = Process(target=consumer, args=(q, 'Coco', '\033[32m'))
- p_l = [p_con1, p_con2, p_pro1, p_pro2, p_pro3]
- [i.start() for i in p_l]
- # 父进程通过等待生产者进程结束后再发送结束标识
- p_pro1.join()
- p_pro2.join()
- p_pro3.join()
- q.put(None) # 几个消费者就要接受几个结束标识
- q.put(None)
- """执行结果
- Kali 拿走了日本制造的智能手机0号
- Coco 拿走了日本制造的智能手机1号
- Kali 拿走了日本制造的智能手机2号
- Coco 拿走了日本制造的智能手机3号
- Kali 拿走了日本制造的智能手机4号
- Coco 拿走了日本制造的智能手机5号
- Kali 拿走了日本制造的智能手机6号
- Coco 拿走了日本制造的智能手机7号
- Kali 拿走了日本制造的智能手机8号
- Coco 拿走了日本制造的智能手机9号
- Kali 拿走了美国制造的智能手机0号
- Coco 拿走了美国制造的智能手机1号
- Kali 拿走了美国制造的智能手机2号
- Coco 拿走了美国制造的智能手机3号
- Kali 拿走了美国制造的智能手机4号
- Coco 拿走了美国制造的智能手机5号
- Kali 拿走了美国制造的智能手机6号
- Coco 拿走了美国制造的智能手机7号
- Kali 拿走了美国制造的智能手机8号
- Coco 拿走了美国制造的智能手机9号
- Kali 拿走了中国制造的智能手机0号
- Coco 拿走了中国制造的智能手机1号
- Kali 拿走了中国制造的智能手机2号
- Coco 拿走了中国制造的智能手机3号
- Kali 拿走了中国制造的智能手机4号
- Coco 拿走了中国制造的智能手机5号
- Kali 拿走了中国制造的智能手机6号
- Coco 拿走了中国制造的智能手机7号
- Kali 拿走了中国制造的智能手机8号
- Coco 拿走了中国制造的智能手机9号
- """
6.进程队列JoinableQueue模块+守护进程实现生产者消费者模型
- from multiprocessing import Process,JoinableQueue
- q = JoinableQueue()
- # q.join() # 用于生产者,等待 q.task_done的返回结果,通过返回结果,生产者就能获得消费者当前消费了多少个数据
- # q.task_done() # 用于消费者,是指每消费队列中一个数据,就给join返回一个标识
- # 假设生产者生产了100个数据,join就能记录下100这个数字,每次消费者消费一个数据,就必须要task_done返回一个标识
- # 当生产者(join)接收到100个消费者返回来的标识的时候,生产者就能知道消费者已经把所有数据都消费完了
- # 消费者
- def consumer(q, name, color):
- while 1:
- info = q.get()
- print('%s %s 拿走了%s \033[0m' % (color, name, info))
- q.task_done()
- # 生产者
- def producer(q, product):
- for i in range(20):
- info = product + '的智能手机%s号' % str(i)
- q.put(info)
- q.join() # 记录了生产了20个数据在队列中,此时会阻塞等待消费者消费完队列中所有数据
- if __name__ == '__main__':
- q = JoinableQueue(10)
- p_pro1 = Process(target=producer, args=(q, '中国制造'))
- p_con1 = Process(target=consumer, args=(q, 'Kali', '\033[31m'))
- p_con1.daemon = True # 把消费者进程设为守护进程
- p_con1.start()
- p_pro1.start()
- p_pro1.join() # 主进程等待生产者进程结束
- # 程序有3个进程,主进程和生产者进程和消费者进程,当主进程执行到p_pro1.join()代码时,主进程会等待生产进程结束
- # 而生产进程中执行到q.join()会等待消费者进程把所有数据消费完,生产者进程才结束
- # 现在的状态就是主进程等待生产者进程结束,生产者进程等待消费者消费完所有数据
- # 所以,把消费者设置为守护进程,当主进程执行完,就代表生产进程已经结束,也就代表消费者进程已经把队列中数据消费完
- # 此时,主进程一旦结束,守护进程也就是消费者进程也就跟着结束,整个程序也就能正常结束了
- """执行结果
- Kali 拿走了中国制造的智能手机0号
- Kali 拿走了中国制造的智能手机1号
- Kali 拿走了中国制造的智能手机2号
- Kali 拿走了中国制造的智能手机3号
- Kali 拿走了中国制造的智能手机4号
- Kali 拿走了中国制造的智能手机5号
- Kali 拿走了中国制造的智能手机6号
- Kali 拿走了中国制造的智能手机7号
- Kali 拿走了中国制造的智能手机8号
- Kali 拿走了中国制造的智能手机9号
- Kali 拿走了中国制造的智能手机10号
- Kali 拿走了中国制造的智能手机11号
- Kali 拿走了中国制造的智能手机12号
- Kali 拿走了中国制造的智能手机13号
- Kali 拿走了中国制造的智能手机14号
- Kali 拿走了中国制造的智能手机15号
- Kali 拿走了中国制造的智能手机16号
- Kali 拿走了中国制造的智能手机17号
- Kali 拿走了中国制造的智能手机18号
- Kali 拿走了中国制造的智能手机19号
- """
4.共享内存-Value, Array
1.概述: 在内存中开辟一段空间存储数据,对多个进程可见,每次写入共享内存的数据会覆盖之前的内容,对内存格式化较少,但存取速度快
2.语法
- from multiprocess import Value
- from multiprocess import Array
- obj = Value(ctype, obj) # 开辟共享内存空间,返回一个共享内存对象
- 参数:
- ctype: str要转变的c类型(对照ctype表)
- obj: 写入共享内存的初始值
- obj.value # 得到共享内存中的值
- obj = Array(ctype, obj) # 开辟共享内存空间(数组),返回一个共享内存对象
- 参数:
- ctype: 要转换的类型
- obj: 存入到共享内存中的数据,obj是一个列表且列表中的数据类型必须一致,obj是正整数则表示开辟一个多大的内存空间
3.进程间通信-Value
- from multiprocessing import Process
- from multiprocessing import Value
- import time
- import random
- # 向共享内存中存取
- def deposit(money):
- for i in range(100):
- time.sleep(0.01)
- money.value += random.randint(1, 100)
- # 向共享内存中取钱
- def withdraw(money):
- for i in range(100):
- time.sleep(0.01)
- money.value -= random.randint(1, 50)
- def main():
- money = Value("i", 1000) # "i"在ctype对照表中对应的数据类型是Python的int类型
- de = Process(target=deposit, args=(money,))
- wi = Process(target=withdraw, args=(money,))
- de.start()
- wi.start()
- de.join()
- wi.join()
- print(money.value) # 本次执行结果money.value的值是3401
- if __name__ == "__main__":
- main()
4.进程间通信-Array
- from multiprocessing import Process
- from multiprocessing import Array
- def func(shm):
- for i in shm:
- print(i, end=" ") # 子进程中打印结果: 1 2 3 4 5
- shm[0] = 11
- def main():
- # 开辟内存共享空间,可容纳5个整数,初始值是[1, 2, 3, 4, 5]
- shm = Array("i", [1, 2, 3, 4, 5]) # "i"在ctype对照表中对应的数据类型是Python的int类型
- # 子内存共享空间开辟一个包含5个整形的空间
- # shm = Array("i", 5) # 没有初始值遍历shm结果为 0 0 0 0 0 此时可以灵活的修改数据
- p = Process(target=func, args=(shm,))
- p.start()
- p.join()
- print()
- for i in shm:
- print(i, end=" ") # 主进程中打印结果: 11 2 3 4 5
- if __name__ == "__main__":
- main()
5.管道消息队列共享内存对比
对比项 | 管道 | 消息队列 | 共享内存 |
开辟空间 | 内存 | 内存 | 内存 |
读写方式 | 双向/单向 | 先进先出 | 操作覆盖内存 |
效率 | 一般 | 一般 | 快 |
应用 | 多用于亲缘进程 | 方便灵活广泛 | 较复杂 |
是否需要互斥机制 | 否 | 否 | 是 |
6.信号-os.kill, signal
1.概述
1.信号的名字,作用和处理信号都是有系统定义的,一个进程向另一个进程通过信号传递某种讯息
2.信号的默认处理方法: 系统定义,信号给接收信号的进程带来的行为一般有 终止 暂停 忽略
3.信号属于异步通信方式,信号的发送不会影响进程的持续执行
4.命令行下信号操作
kill -l: 查看信号
kill -signame PID: 给PID的进程发送一个信号, 例如 kell -9 2332
2.常用信号
- # 默认操作: 终止的信号
- SIGHUP: 该信号在用户终端连接(正常或非正常)结束时发出,通常是在终端的控制进程结束时,通知同一会话内的各个作业与控制终端不再关系
- SIGINT: 该信号在用户键入INTR字符(通常是Ctrl+c)时发出,终端驱动程序发送此信号并送到前台进程中的每一个进程
- SIGQUIT: 该信号和SIGINT类似,但由QUIT字符(通常是'"Ctrl+\"')来控制
- SIGILL: 该信号在一个进程企图执行一条非法指令时(可执行文件本身出现错误,或者试图执行数据段,堆栈溢出时)发出
- SIGFPE: 该信号在发送致命的算术运算错误时发出,不仅包括浮点运算错误还包括溢出和除数为0等其他所有的算数的错误
- SIGKILL: 该信号用来立即结束程序的运行,并且不能被阻塞,处理和忽略
- SIGALRM: 该信号当一个定时器到时的时候发出
- SIGABORT: 该信号用于结束进程
- # 默认操作: 暂停进程
- SIGSTOP: 该信号用于暂停一个进程,并且不能被阻塞,处理和忽略
- SIGTSTP: 该信号用于暂停交互进程,用户可以键入SUSP字符(通常是Ctrl+z)发出这个信号
- # 默认操作: 忽略
- SIGCHLD: 子进程改变状态时,父进程会收到这个信号
3.发送信号
语法
- import signal
- os.kill(pid, sig) # 向一个进程发送一个信号
- 参数:
- pid: 要发送信号的进程PID
- sig: 要发送的信号
- signal.alarm(sec) # 向自身发送一个时钟信号SIGALRM,一个进程中只能同时有一个时钟,后面的时钟时间会覆盖前面的时钟时间
- 参数: sec代表时钟秒数
示例1
- import os
- import signal
- # 向 2332 进程发送SIGKILL信号,杀死2332进程
- os.kill(2332, signal.SIGKILL)
示例2
- import time
- import signal
- # 3秒钟之后向自身发送SIGALRM信号,终止进程
- signal.alarm(3)
- time.sleep(1)
- signal.alarm(5) # 后面的时钟时间会覆盖前面的时钟时间
- while True:
- time.sleep(1)
- print("等待时钟...")
4.信号处理
语法
- import signal
- signal.pause() # 阻塞等待一个信号的发生
- signal.signal(signum, handler) # 处理一个信号
- 使用说明: 是一个异步处理信号的函数,只要执行在进程中就会按照指定方法处理信号,但是不能处理 SIGSTOP 和 SIGKILL 信号
- 参数:
- signum: 要处理的信号
- handler: 对该信号的处理方法
- SIG_DFL: 采用默认方法
- SIG_IGN: 忽略这个信号
- func: 回调函数,自定义处理方法
- 自定义处理方法格式要求
- def func(sig, frame): # sig: 接收到的信号;frame: 信号对象
- pass
示例1
- import time
- import signal
- # 5秒钟之后向自身发送SIGALRM信号,终止进程
- signal.alarm(5)
- # 采用默认的方法处理SIGALRM信号
- signal.signal(signal.SIGALRM, signal.SIG_DFL)
- # 采用忽略信号的方法处理SIGALRM信号
- # signal.signal(signal.SIGALRM, signal.SIG_IGN)
- # signal.signal(signal.SIGINT, signal.SIG_IGN) # 忽略Ctrl+c终止进程,即进程运行时无法使用Ctrl+c来终止
- while True:
- time.sleep(1)
- print("等待时钟...")
示例2
- import time
- import signal
- # 5秒钟之后向自身发送SIGALRM信号,终止进程
- signal.alarm(5)
- # 固定格式要求
- def handler(sig, frame):
- if sig == signal.SIGALRM:
- print("收到了时钟信号: %s" % sig)
- elif sig == signal.SIGINT:
- print("收到了时钟信号: %s" % sig)
- # 通过自定义方法handler处理SIGALRM信号
- signal.signal(signal.SIGALRM, handler)
- signal.signal(signal.SIGINT, handler)
- while True:
- time.sleep(1)
- print("等待时钟...")
5.使用信号处理僵尸进程
import signal
原理: 在父进程中忽略子进程的发送信号
语法: signal.signal(signal.SIGCHLD, signal.SIG_IGN)
6.多进程实现信号通信
- import multiprocessing
- from signal import *
- from time import sleep
- import os
- # 售票员处理信号
- def conductor_handler(sig, frame):
- if sig == SIGINT:
- os.kill(os.getppid(), SIGUSR1)
- elif sig == SIGQUIT:
- os.kill(os.getppid(), SIGUSR2)
- elif sig == SIGUSR1:
- print("到站了")
- os._exit(0)
- # 子进程中售票员发送信号
- def conductor():
- signal(SIGINT, conductor_handler)
- signal(SIGQUIT, conductor_handler)
- signal(SIGUSR1, conductor_handler)
- # 子进程中忽略父进程要处理的信号
- signal(SIGTSTP, SIG_IGN)
- while True:
- sleep(3)
- print("车内开始广播...")
- # 司机处理信号
- def driver_handler(sig, frame):
- if sig == SIGUSR1:
- print("老司机开车")
- elif sig == SIGUSR2:
- print("老司机以锁死车门")
- elif sig == SIGTSTP:
- os.kill(p_pid, SIGUSR1)
- def main():
- p = multiprocessing.Process(target=conductor)
- p.start()
- global p_pid
- p_pid = p.pid
- # 父进程中司机发送信号
- signal(SIGUSR1, driver_handler)
- signal(SIGUSR2, driver_handler)
- signal(SIGTSTP, driver_handler)
- # 父进程忽略子进程要处理的信号
- signal(SIGINT, SIG_IGN)
- signal(SIGQUIT, SIG_IGN)
- p.join()
- if __name__ == "__main__":
- main()
- """执行结果
- ~/Desktop/python3/demo $ python3 demo5.py
- 车内开始广播...
- 车内开始广播...
- ^C老司机开车
- ^C老司机开车
- ^C老司机开车
- 车内开始广播...
- ^\老司机以锁死车门
- ^\老司机以锁死车门
- ^\老司机以锁死车门
- 车内开始广播...
- 车内开始广播...
- ^Z到站了
- """
7.信号量-Semaphore
1.概述: 给定一定的信号数量,对多个进程可见并且多个进程均可操作,进程根据信号量的多少可以有不同的行为
2.语法
- from multiprocess import Semaphore
- sem = Semaphore(num) # 定义信号量并返回信号量对象,num是给定信号量的初始个数
- sem.acquire() # 将信号量减一,信号量为0时调用次方法会阻塞等待
- sem.release() # 将信号量加一
3.示例1
- import time
- import multiprocessing
- def func(sem):
- print("进程%s等待信号量" % multiprocessing.current_process()) # current_process获取当前进程对象
- sem.acquire() # 信号量减1
- print("进程%s消耗信号量" % multiprocessing.current_process())
- time.sleep(2)
- print("进程%s添加信号量" % multiprocessing.current_process())
- sem.release() # 信号量加1
- def main():
- # 创建信号量初始值为3
- sem = multiprocessing.Semaphore(3)
- jobs = list()
- for i in range(4):
- p = multiprocessing.Process(target=func, args=(sem,))
- p.start()
- jobs.append(p)
- for i in jobs:
- i.join()
- if __name__ == "__main__":
- main()
- """执行结果
- 进程<Process(Process-1, started)>等待信号量
- 进程<Process(Process-1, started)>消耗信号量
- 进程<Process(Process-2, started)>等待信号量
- 进程<Process(Process-2, started)>消耗信号量
- 进程<Process(Process-3, started)>等待信号量
- 进程<Process(Process-3, started)>消耗信号量
- 进程<Process(Process-4, started)>等待信号量
- 进程<Process(Process-1, started)>添加信号量
- 进程<Process(Process-2, started)>添加信号量
- 进程<Process(Process-4, started)>消耗信号量
- 进程<Process(Process-3, started)>添加信号量
- 进程<Process(Process-4, started)>添加信号量
- """
4.示例2
- from multiprocessing import Process
- from multiprocessing import Semaphore
- import time
- import random
- def func(i, sem):
- sem.acquire()
- print('第%s个人进入小黑屋,拿了钥匙锁上门' % i)
- time.sleep(random.randint(3, 5))
- print('第%s个人出去小黑屋,还了钥匙打开门' % i)
- sem.release()
- if __name__ == '__main__':
- sem = Semaphore(5) # 初始化了一把锁5把钥匙,也就是说允许5个人同时进入小黑屋
- # 之后其他人必须等待,等有人从小黑屋出来,还了钥匙,才能允许后边的人进入
- for i in range(20):
- p = Process(target=func, args=(i, sem,))
- p.start()
8.共享数据-Manager
1.概述
Python中提供了强大的Manager类,专门用于实现多进程之间的数据共享
Manager类是数据不安全的,Manager类包含的常用方法和属性与Multiprocessing中其他常用类的方法属性一致
Manager管理的共享数据类型有: Value, Array, dict, list, Lock, Semaphore等等,同时Manager还可以共享类的实例对象
2.Manager实现dict功能
- from multiprocessing import Manager, Process, Lock
- def work(d, lock):
- with lock: # 不加锁而操作共享的数据,肯定会出现数据错乱
- d['count'] -= 1
- if __name__ == '__main__':
- lock = Lock()
- with Manager() as m:
- dic = m.dict({'count': 100})
- p_l = []
- for i in range(100):
- p = Process(target=work, args=(dic, lock))
- p_l.append(p)
- p.start()
- for p in p_l:
- p.join()
- print(dic)
3.Manager实现list功能
- from multiprocessing import Manager, Process
- def func(num):
- num[0] -= 1
- print("子进程中num的值是%s" % num)
- if __name__ == "__main__":
- m = Manager()
- num = m.list([1, 2, 3])
- p = Process(target=func, args=(num,))
- p.start()
- p.join()
- print("父进程中num的值是%s" % num)
4.通过Manager进程间共享实例对象
- from multiprocessing import Process, Value, Lock
- from multiprocessing.managers import BaseManager
- class Employee(object):
- def __init__(self, name, salary):
- self.name = name
- self.salary = Value('i', salary)
- def increase(self):
- self.salary.value += 100
- def getPay(self):
- return self.name + ':' + str(self.salary.value)
- class MyManager(BaseManager):
- pass
- def Manager2():
- m = MyManager()
- m.start()
- return m
- MyManager.register('Employee', Employee)
- def func1(em, lock):
- with lock:
- em.increase()
- if __name__ == '__main__':
- manager = Manager2()
- em = manager.Employee('zhangsan', 1000)
- lock = Lock()
- proces = [Process(target=func1, args=(em, lock)) for i in range(10)]
- for p in proces:
- p.start()
- for p in proces:
- p.join()
- print(em.getPay())
05_进程间通信 IPC的更多相关文章
- Linux进程间通信IPC学习笔记之同步二(SVR4 信号量)
Linux进程间通信IPC学习笔记之同步二(SVR4 信号量)
- Linux进程间通信IPC学习笔记之同步二(Posix 信号量)
Linux进程间通信IPC学习笔记之同步二(Posix 信号量)
- Linux进程间通信IPC学习笔记之消息队列(SVR4)
Linux进程间通信IPC学习笔记之消息队列(SVR4)
- Android进程间通信IPC
一.IPC的说明 IPC是Inter-Process Communication的缩写,含义为进程间通信或跨进程通信,是指两个进程之间进行数据交换的过程. IPC不是Android独有的,任何一个操作 ...
- 进程间通信IPC -- 管道, 队列
进程间通信--IPC(Inter-Process Communication) 管道 from multiprocessing import Pipecon1,con2 = Pipe()管道是不安全的 ...
- [原创]chromium源码阅读-进程间通信IPC.消息的接收与应答
chromium源码阅读-进程间通信IPC.消息的接收与应答 chromium源码阅读-进程间通信IPC.消息的接收与应答 介绍 chromium进程间通信在win32下是通过命名管道的方式实现的 ...
- 进程间通信IPC之--无名管道(pipe)和有名管道(fifo)(转)
进程间通信IPC之--无名管道(pipe)和有名管道(fifo) 2012-01-17 22:41:20 分类: C/C++ 每个进程各自有不同的用户地址空间,任何一个进 程的全局变量在另一个进程中 ...
- 进程间通信IPC、LPC、RPC
进程间通信(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法.进程是计算机系统分配资源的最小单位.每个进程都有自己的一部分独立的系 ...
- 【Android】进程间通信IPC——Binder
Binder是Android中的跨进程通信方式,bindService的时候,服务端返回Binder对象,通过该对象客户端可以从服务端获取数据.在进程间通信IPC——AIDL中创建了ICustomAi ...
随机推荐
- day25:魔术方法
目录 1.__del__(析构方法) 2.魔术方法:__str__ 3.魔术方法:__repr__ 4.魔术方法:__call__ 5.魔术方法:__bool__ 6.魔术方法:__add__& ...
- Linux文本处理详细教程
1. 文本处理 本节将介绍Linux下使用Shell处理文本时最常用的工具: find.grep.xargs.sort.uniq.tr.cut.paste.wc.sed.awk: 提供的例子和参数都是 ...
- XCTF-WEB-高手进阶区(1-4)笔记
1:baby_web 题目描述:想想初始页面是哪个 通过Dirsearch软件扫描发现Index.php被藏起来了,访问他便会指向1.php 于是通过Burp修改Get为index.php,然后放入R ...
- java 多态一
一 多态的概述 现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学 生也是人,即出现两种形态. Java作为面向对象的语言,同样可以描述一个事物的多种形态.如Studen ...
- C#LeetCode刷题之#896-单调数列(Monotonic Array)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3760 访问. 如果数组是单调递增或单调递减的,那么它是单调的. ...
- TypeScript是什么,为什么要使用它?
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文出处:https://medium.com/swlh/what-is-typescript-bf333e ...
- DUBBO学习心得
项目环境版本:dubbo2.5.10 spring版本4.3.10 一 SOA 1英文名称(Service Oriented Ambiguity) 2 中文名称:面向服务架构 2.1 有一个专门提 ...
- ybt1107题解和方法总结
今天花了三个小时的时间刷了些基础题,虽说是简单题,但是有一些还是有点难度的 比如ybt1107,我死嗑了半个小时. [题目描述] 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米. ...
- String、StringBuilder、StringBuffer三者的区别
StringBuffer.StringBuilder和String都可以用来代表字符串.String类是不可变类,任何对String的改变都会引发新的String对象的生成:StringBuffer. ...
- 【API进阶之路】逆袭!用关键词抽取API搞定用户需求洞察
摘要: 老大说,我这份用关键词抽取API搞定的用户需求洞察报告,简直比比市场调研的科班人士做得还好. 最近这半个月的午饭,那可是相当不错,市场老大天天请吃饭,不是外面下馆子,就是从家带饺子.说是感谢我 ...