多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。
不同进程之间内存是不共享的,要实现两个进程间的数据交换,可以用以下方法:
queues
使用方法和threading里面的queue差不多
from multiprocessing import Process,Queue def f(q): q.put([2,None,'hello']) if __name__ =='__main__': q = Queue() p = Process(target=f,args=(q,)) p.start() print(q.get()) p.join()
运行结果
[2, None, 'hello']
多进程中,对于一个变量,每个进程都是复制了一份,所以每个进程之间修改数据互不影响。 Queue()方法相当于第三方,把进程A的数据序列化后传给进程B 反序列化得到数据。并不是一个共享的变量。而是实现了数据的传递。
Pipes 管道
类似于socket 一端发送,一端接收,实现通信。
from multiprocessing import Process,Pipe def f(conn): conn.send([5,'hello']) conn.close() if __name__ =='__main__': parent_conn,child_conn = Pipe() p = Process(target=f,args=(child_conn,)) p.start() print(parent_conn.recv()) p.join()
运行结果
[5, 'hello']
发送方和接收方的关系,也和socket类似,发送方发送几次,接收方就要接收几次。接收方如果接收的次数多于发送方,那么接收方就会卡住,直到发送方在发送一次。
相互通信
def f(conn): conn.send([5,'hello']) #发送数据 print(conn.recv()) #接收数据 conn.close() if __name__ =='__main__': parent_conn,child_conn = Pipe() p = Process(target=f,args=(child_conn,)) p.start() print(parent_conn.recv()) #接收数据 parent_conn.send("hehe你好") #发送数据 p.join()
Managers
由manager()返回的manager对象控制一个包含Python对象的服务器进程,并允许其他进程使用代理来操作它们。
由manager()返回的管理器将支持类型列表、命令、名称空间、锁、RLock、信号量、BoundedSemaphore、Condition、Event、Barrier、Queue、Value和Array。
from multiprocessing import Process, Manager def f(d, l): d[1] = '1' d['2'] = 2 d[0.25] = None l.append(1) print(l) if __name__ == '__main__': with Manager() as manager: d = manager.dict() #生成一个字典,可以在多个进程中传递和共享。 l = manager.list(range(5)) #生成一个列表,在多个进程中传递和共享。 p_list = [] #存放进程对象 for i in range(10): p = Process(target=f, args=(d, l)) p.start() p_list.append(p) for res in p_list: res.join() #等待进程结束 print(d) print(l)
运行结果
[0, 1, 2, 3, 4, 1] [0, 1, 2, 3, 4, 1, 1] [0, 1, 2, 3, 4, 1, 1, 1] [0, 1, 2, 3, 4, 1, 1, 1, 1] [0, 1, 2, 3, 4, 1, 1, 1, 1, 1] [0, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1] [0, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1] [0, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1, 1] [0, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1] [0, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] {0.25: None, 1: '1', '2': 2} [0, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
以上实现了进程之间的数据共享,不是数据传递,而是真正的共享。并且可以同时修改。
Manager()内部有加锁机制,不允许两个进程同时修改一份数据,因为进程的数据是独立的。