实例一:
一个简单的多进程实例,要创建进程,需要motiproceing模块。用法和创建线程的差不多。这个程序,实现了创建5个进程,然后每个进程再创建了一个线程。
import multiprocessing
import time
import threading
def f(name):
time.sleep(2)
print("hello",name)
t=threading.Thread(target=thread_run,)
t.start()
def thread_run():
print(threading.get_ident())
if __name__ == '__main__':
for i in range(5):
p=multiprocessing.Process(target=f,args=('bob %s'%i,))
p.start()
F:\anaconda\python.exe F:/web/s14/进程、线程/noke.py
hello bob 3
hello bob 0
3820
1992
hello bob 1
3516
hello bob 2
1256
hello bob 4
3744
实例二:
类似于上面的程序,只不过实现了打印当前进程的父进程号和当前进程号,然后在当前进程下创建了子进程,然后打印子进程的父进程号,再打印该子进程号,会得出:每一个子进程都是由父进程启动的
from multiprocessing import Process
import os
def info(title):
print(title)
print('module name:', __name__)
print('parent process:', os.getppid())
print('process id:', os.getpid())
print("\n\n")
def f(name):
info('\033[31;1mfunction f\033[0m')
print('hello', name)
if __name__ == '__main__':
info('\033[32;1mmain process line\033[0m')
p = Process(target=f, args=('bob',))
p.start()
p.join()
F:\anaconda\python.exe F:/web/s14/进程、线程/noke.py
main process line
module name: __main__
parent process: 1268
process id: 2108
function f
module name: __mp_main__
parent process: 2108
process id: 4040
hello bob
实例三:进程间数据共享的第一种方法:队列
下面这个程序,通过队列,实现了线程间数据共享,注意:这个队列只是普通的队列,没有进行任何封装
queue,线程间的queue是实现线程间生产者消费者模型的,但是只能在当前程序的线程里访问
import queue
import threading
def f(q):
q.put([42, None, 'hello'])
if __name__ == '__main__':
q = queue.Queue()
p = threading.Thread(target=f, args=(q,))
p.start()
print(q.get())
p.join()
下面这个程序,并没有使用正常的queue。使用了封装后的queue
原理:相当于克隆了一个queue。因为在父进程创建子进程的时候放进去queue, 通过一个中间内存,pickle一下序列化一下,再反序列化一下。只是实现了数据的传递
from multiprocessing import Process, Queue
import queue #不能是线程queue
def f(q):
q.put([42, None, 'hello'])
if __name__ == '__main__':
q = Queue()
p = Process(target=f, args=(q,)) #虽然进程间内存不共享,但是我们可以在进程出生时,其他进程给新进程传数据
p.start()
print(q.get()) # prints "[42, None, 'hello']"
p.join()
实例四:进程间数据共享的第二种方法:管道
from multiprocessing import Process, Pipe
def f(conn):
conn.send([42, None, 'hello'])
conn.send([42, None, 'hello2'])
conn.send([42, None, 'hello3'])
conn.close()
if __name__ == '__main__':
parent_conn, child_conn = Pipe()
p = Process(target=f, args=(child_conn,))
p.start()
print(parent_conn.recv())
print(parent_conn.recv())
print(parent_conn.recv())
p.join()
F:\anaconda\python.exe F:/web/s14/进程、线程/noke.py
[42, None, 'hello']
[42, None, 'hello2']
[42, None, 'hello3']
实例五:
刚才只是两个进程间数据的传递
那么,如何向在多线程一样同时修改数据呢?
使用manager,注意:不需要加锁,manager默认加锁了,共享数据的原理和queue原理一样
from multiprocessing import Process, Manager
import os
def f(d, l):
d[os.getpid()]=os.getpid()
l.append(os.getpid())
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)
实例六:进程同步:加锁的目的,控制打印顺序(对屏幕进行加锁)
进程里面为什么需要锁?
虽然每个进程都是独立的,但是对于屏幕来说,屏幕不是共享的,控制打印顺序
在windows里面试不出来,在linux里面可以试出来
存在的意义:就是在屏幕上打印
from multiprocessing import Process, Lock
def f(l, i):
l.acquire()
print('hello world', i)
l.release()
if __name__ == '__main__':
lock = Lock()
for num in range(10):
Process(target=f, args=(lock, num)).start()
F:\anaconda\python.exe F:/web/s14/进程、线程/noke.py
hello world 3
hello world 6
hello world 7
hello world 8
hello world 2
hello world 9
hello world 0
hello world 1
hello world 4
hello world 5
实例七:进程池的使用(生产中常用的)
我们启动多进程明显很慢,因为开销特别大,所以我们使用线程池。注意:启动特别多的线程仅仅是cpu上下文切换变多,一般情况,系统只是变慢,而启动特别多的进程,容易把系统搞瘫痪
from multiprocessing import Process, Pool,freeze_support
import time
import os
def Foo(i):
time.sleep(2)
print("in process ",os.getpid())
return i + 100
def Bar(arg):
print('-->exec done:', arg,os.getpid())
if __name__ == '__main__': #为了区分是主动执行这个脚本,还是导入的模块 意思就是这样写,手动执行这个脚本,就会执行,而导入不会执行
print("主进程 pid",os.getpid())
pool = Pool(3) #允许进程池里同时放入5个进程,放入进程池里的进程才会运行,其他进程挂起
for i in range(10):
pool.apply_async(func=Foo, args=(i,), callback=Bar) #并行 ,callback回调 干完foo才能干bar,干不完foo不干bar。 主进程调用的回调。备份完毕自动向数据库写日志。为什么不在子进程写日志?因为父进程只连一次,而子进程要连10次。
#pool.apply(func=Foo, args=(i,)) #串行
print('end')
pool.close() #一定要先关闭进程池再join
pool.join() # 进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭,不等进程执行完毕程序直接关闭 先close在join