python并发编程之多进程multiprocessing模块(实践篇)笔记

一 multiprocessing模块介绍

python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。Python提供了multiprocessing。

multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

二 Process类的介绍与使用

通过创建一个 Process 对象然后调用它的 start() 方法来生成进程。 Process 和 threading.Thread API 相同。

Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
#参数说明
1 group参数未使用,值始终为None
2 target表示调用对象,即子进程要执行的任务
3 args表示调用对象的位置参数元组,args=(1,2,'anne',)是一个元组形式,x结尾必须有逗号
4 kwargs表示调用对象的字典,kwargs={'name':'anne','age':18}
5 name为子进程的名称

三Process类的方法

run()
表示进程活动的方法。

你可以在子类中重载此方法。标准 run() 方法调用传递给对象构造函数的可调用对象作为目标参数(如果有),分别从 args 和 kwargs 参数中获取顺序和关键字参数。

start()
启动进程活动。

这个方法每个进程对象最多只能调用一次。它会将对象的 run() 方法安排在一个单独的进程中调用。

join([timeout])
如果可选参数 timeout 是 None (默认值),则该方法将阻塞,直到调用 join() 方法的进程终止。如果 timeout 是一个正数,它最多会阻塞 timeout 秒。请注意,如果进程终止或方法超时,则该方法返回 None 。检查进程的 exitcode 以确定它是否终止。

p1.join() #表示执行完进程P1再执行主进程

主进程等待子进程,等待的是主进程,所以等待的总时间是子进程中耗费时间最长的那个进程运行的时间

一个进程可以被 join 多次。

进程无法join自身,因为这会导致死锁。尝试在启动进程之前join进程是错误的。
daemon()方法:守护进和,

 p2.daemon=True  #守护进程,表示主进程结束p2就结束

进程间的通信 Queue(对列)Pipe(通道)

进程之间数据不共享, 虽然可以用文件共享数据实现进程间通信,但问题是:1)效率低(共享数据基于文件,而文件是硬盘上的数据) 2)需要自己加锁处理
mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道

创建队列的类(底层就是以管道和锁定的方式实现):
Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。

相当于创建立了一个堆栈,通过put方法存入数据,get 方法取出数据 ,empty 方法判断队列是否为空

创建管道的类:
Pipe([duplex]):在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象,强调一点:必须在产生Process对象之前产生管道

参数介绍:
dumplex:默认管道是全双工的,如果将duplex射成False,conn1只能用于接收,conn2只能用于发送。

# 管道可以用于双向通信,利用通常在客户端/服务器中使用的请求/响应模型或远程过程调用,就可以使用管道编写与进程交互的程序
from multiprocessing import Process,Pipe
import time,os
def adder(p,name):
    server,client=p
    client.close()
    while True:
        try:
            x,y=server.recv()
        except EOFError:
            server.close()
            break
        res=x+y
        server.send(res)
    print('server done')
if __name__ == '__main__':
    server,client=Pipe()

    c1=Process(target=adder,args=((server,client),'c1'))
    c1.start()

    server.close()

    client.send((10,20))
    print(client.recv())
    client.close()

    c1.join()
    print('主进程')
#注意:send()和recv()方法使用pickle模块对对象进行序列化。

Pool 进程池类

可以创建一个进程池
class multiprocessing.pool.Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])
参数
1 numprocess:要创建的进程数,如果省略,将默认使用cpu_count()的值
2 initializer:是每个工作进程启动时要执行的可调用对象,默认为None
3 initargs:是要传给initializer的参数组
一个进程池对象,它控制可以提交作业的工作进程池。它支持带有超时和回调的异步结果,以及一个并行的 map 实现。创建进程池的类:如果指定numprocess为3,则进程池会从无到有创建三个进程,然后自始至终使用这三个进程去执行所有任务,不会开启其他进程
Pool内的进程数默认是cpu核数,假设为4(查看方法os.cpu_count())
开启6个客户端,会发现2个客户端处于等待状态
在每个进程内查看pid,会发现pid使用为4个,即多个客户端公用4个进程
主要方法:
1 p.apply(func [, args [, kwargs]])
在一个池工作进程中执行func(*args,**kwargs),然后返回结果。
需要强调的是:此操作并不会在所有池工作进程中并执行func函数。如果要通过不同参数并发地执行func函数,必须从不同线程调用p.apply()函数或者使用p.apply_async()
2 p.apply_async(func [, args [, kwargs]]):
在一个池工作进程中执行func(*args,**kwargs),然后返回结果。
此方法的结果是AsyncResult类的实例,callback是可调用对象,接收输入参数。当func的结果变为可用时,
将理解传递给callback。callback禁止执行任何阻塞操作,否则将接收其他异步操作中的结果。
3 p.close():关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成
4 P.jion():等待所有工作进程退出。此方法只能在close()或teminate()之后调用

from multiprocessing import Pool
import requests
import json
import os

def get_page(url):
    print('<进程%s> get %s' %(os.getpid(),url))
    respone=requests.get(url)
    if respone.status_code == 200:
        return {'url':url,'text':respone.text}

def pasrse_page(res):
    print('<进程%s> parse %s' %(os.getpid(),res['url']))
    parse_res='url:<%s> size:[%s]\n' %(res['url'],len(res['text']))
    with open('db.txt','a') as f:
        f.write(parse_res)


if __name__ == '__main__':
    urls=[
        'https://www.baidu.com',
        'https://www.python.org',
        'https://www.openstack.org',
        'https://help.github.com/',
        'http://www.sina.com.cn/'
    ]

    p=Pool(3)
    res_l=[]
    for url in urls:
        res=p.apply_async(get_page,args=(url,),callback=pasrse_page)
        res_l.append(res)

    p.close()
    p.join()
    print([res.get() for res in res_l]) #拿到的是get_page的结果,其实完全没必要拿该结果,该结果已经传给回调函数处理了

实例

做的阿里千牛的自动回复程序


import RobotCL,time,pythoncom
import sys
from ctypes import *
from ctypes.wintypes import MSG
from ctypes.wintypes import DWORD
from KeyboardHook import *
from multiprocessing import Process,Queue
q = Queue()
#定义钩子处理函数
#参数nCode是hook类型 ,wParam和lParam参数的值依赖于Hook代码包含了关于发送或者接收消息的信息。
#keyLogger
def hookProc(nCode, wParam, lParam):
    print ('nCode=',nCode, 'wParam=',wParam, 'lParam=',lParam)
    print(user32.GetKeyState(0x45))
    if wParam is not WM_KEYDOWN: #如果不是按键消息就放过
        user32.CallNextHookEx(keyLogger.hooked, nCode, wParam, lParam)
        return False
    if(CTRL_CODE == int(0xFFFFFFFF&lParam[0])):#如果按下CTRL就结束
        print("Ctrl pressed, call uninstallHook()")
        keyLogger.uninstallHookProc()
        user32.CallNextHookEx(keyLogger.hooked, nCode, wParam, lParam)
       # q.put(True)
        keyLogger.endKeyLog()  #结束消息循环
        return True
        #sys.exit(-1)
    hookedKey = chr(0xFFFFFFFF&lParam[0]) #chr函数返回指定的字符代码的对应字符
    print("按键",hookedKey)
    #q.put(False)
    #print("hookProcisempty=", q.empty())
    user32.CallNextHookEx(keyLogger.hooked, nCode, wParam, lParam)
    return  False

#实例化一个hook类
keyLogger = KeyLogger()
hp=hookProc
#取得回调函数指针
pointer = keyLogger.getFPTR(hp)

def hook(q):
    if keyLogger.installHookProc(pointer):
           print("installed keyLogger")
    print('keyLogger=',keyLogger)
    pythoncom.PumpMessages(10000)
    #keyLogger.startKeyLog()
    q.put(True)
    print("hookisempty=", q.empty())



def Robot(q):
    while True:
        print('开始')
        RobotA=RobotCL.RobotCL() #定义机器人
        RobotA.Activate_window() #激活窗口
        if RobotA.Select_friends(): #有新信息
            RobotA.Activate_window()  # 激活窗口
            RobotA.Input_text()   #输入并发送并关闭对话
        time.sleep(10)
        if not q.empty():
            isctrl=q.get()
            print("isctrl=",isctrl)
            if isctrl:break
        print('重新开始')
def test(q):
    while True:
        print("testisempty=", q.empty())
        if not q.empty():
            isctrl = q.get()
            print("isctrl=", isctrl)
            if isctrl:
                break
        time.sleep(5)
        print('重新开始')



if __name__ == '__main__':

    p1=Process(target=hook,args=(q,)) #必须加,号
    #p2=Process(target=test,args=(q,))
    p2=Process(target=Robot,args=(q,))
    p2.daemon=True  #守护进程,主进程结束p2就结束
    p1.start()
    p2.start()
    p1.join()
    #Ap2.join()

    #q.put(True)
    test(q)
    while True:
        if not q.empty():
            print("q的内容", q.get())
        if  q.empty():
            print('q打空')
            break
    print('主线程')

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值