使用模块

multiprocessing

简单例子

import multiprocessing

def test(n):
    name = multiprocessing.current_process().name
    print(name,"starting")
    print("is",n)
    return

if __name__ == '__main__':
    num_list =[]
    for i in range(10):
        p = multiprocessing.Process(target=test,args=(i,))
        num_list.append(p)
        p.start()
        #join是阻塞主进程等待子进程返回
        p.join()
        print(multiprocessing.cpu_count())
        print("End")
    print(num_list)

上面的例子中使用multiprocessing中的Process来创建进程,target为执行的方法,传入函数,args为方法需要传入的参数,此外还可设置group,name等,group一般不会用到,join为阻塞主进程,cpu_count()来获取copu的核数。name来命名子进程。进程有创建,就绪,运行,阻塞与结束共5个状态,start()方法为就绪并不是运行,告知CPU让系统来进行调度安排。

进程池的使用

from multiprocessing import Pool

def func(n):
    return n**2

if __name__ == '__main__':
    pool = Pool(processes=4)
    #异步非阻塞
    result = pool.apply_async(func,[10])
    print(result.get(timeout=1))
    print(pool.map(func,range(10)))

processes意思为开4个进程,一个cpu开一个进程,apply_async为异步启动,如一个任务有4个步骤,使用异步的话就不需要按顺序执行,执行1的同时可以执行2,不需要等待。进程池里还有close()和join()、map()等方法,close()为禁止往进程池添加任务,join()与普通的多进程相仿,map(func,iterate)与python中的map一样迭代队列执行func。

多进程队列

from multiprocessing import Process,Queue
def set_data(queue):
    for i in range(10):
        queue.put("hello"+str(i))

def get_data(queue):
    for i in range(10):
        print(queue.get())

if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=set_data,args=(q,))
    p2 = Process(target=get_data,args=(q,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print("空...空了吗?",q.empty())

一个进程往队列添加数据,一个进程取走数据,使用这种方法在爬虫之中,可以一个进程存储未爬取的url,一个进程获取,来提高爬取数据的速度。

进程锁

from multiprocessing import Process,Lock,Semaphore

def test_lock(lock,n):
    lock.acquire()#获得锁
    print("this is ",n)
    lock.release()#最后解锁释放

if __name__ == '__main__':
    lock = Lock()
    # s = Semaphore(3)
    for i in range(10):
        Process(target=test_lock,args=(lock,i)).start()

在多进程中,可能在同一时间对一个变量进行访问修改,这时候需要加把锁来锁住,防止一边在修改并且提交了修改后的数据,另一边访问到未修改前的数据并对其修改。加锁可以保证数据的稳定性与安全性,multiprocessing中有2种方式加锁,一种是Lock,一种是Semaphore,后者可以设置同时多少个进程进行访问。

消息传递

from multiprocessing import Process,Event

def wait(event):
    print("wait~~~")
    event.wait()
    print("event.is_set",event.is_set())

def wait_timeout(event,t):
    print("timeout!!!")
    print()
    print("event.is_set", event.is_set())
    event.set()#设置为真

if __name__ == '__main__':
    event = Event()
    print(event.is_set())
    t1 = Process(target=wait,args=(event,))
    t1.start()
    t2 = Process(target=wait_timeout,args=(event,2))
    t2.start()
    print("set event")

在multiprocessing中可以使用Event来进行进程间的通讯,通过is_set()方法来获取当前状态来进行下一步的操作,wait()方法为阻塞等待,如果未接到可执行信号时将一直阻塞。timeout()顾名思义则是超时设置,在超过多少时间后进行信号的设置,然后进行下一步的操作。可以把wait_timeout中的event.set()注释掉来查看具体的效果。

管道

from multiprocessing import Process,Pipe

def p1(pipe):
    pipe.send('pipe1')
    print("pipe1 received: %s"%pipe.recv())
    pipe.send("who are you")
    print("pipe1 received: %s"%pipe.recv())

def p2(pipe):
    pipe.send('pipe2')
    print("pipe2 received: %s"%pipe.recv())
    pipe.send("this is a bad problem")
    print("pipe2 received: %s"%pipe.recv())

if __name__ == '__main__':
    pipe = Pipe()
    #第一个管道对象传入第一个进程
    process1 = Process(target=p1,args=(pipe[0],))
    # 第二个管道对象传入第二个进程
    process2 = Process(target=p2,args=(pipe[1],))
    process1.start()
    process2.start()
    process1.join()
    process2.join()

管道可以看做是两个进程之间进行消息的互通,可以设置单向管道,默认为双向的管道,管道的端口智能由一个进程来使用,否则出现错误。用send来发送消息,recv来接收消息。

多进程共享变量

from multiprocessing import Process,Value,Array

def func(n,a):
    n.value = 200
    for i in range(len(a)):
        a[i] = -a[i]
if __name__ == '__main__':
    num = Value('d',0.0)
    print(num.value)
    arr = Array('i',range(10))
    print(arr[:])
    p = Process(target=func,args=(num,arr))
    p.start()
    p.join()
    print(num.value)
    print(arr[:])

在multiprocessing中使用Value和Array来进行变量的共享,value是创建变量大小的共享内存,Array是创建数组大小的共享内存,第一个参数为创建的类型。进程之中的变量为独立的,而num和arr为主进程的变量,通过Value和Array,可以在子进程中进行修改。