Python进程

每日一记

1,GIL(全局解释器锁)导致线程不能利用多核优势
2,多进程实现方式:函数式,继承式
3,multiprocessing模块
multiprocessing模块介绍:
python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),
在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing。
multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),
该模块与多线程模块threading的编程接口类似。
multiprocessing模块的功能众多:支持子进程、通信和共享数据、
执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。
需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内。
进程中的方法:
terminate:强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,
使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。
timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,
而不能join住run开启的进程
is_alive:如果p仍然运行,返回True
daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,
p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
name:进程的名称
pid:进程的pid
4,进程同步(锁)
进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的。
共享同一打印终端,发现会有多行内容打印到一行的现象(多个进程共享并抢占同一个打印终端,乱了)
既然可以用文件共享数据,那么进程间通信用文件作为数据传输介质就可以了啊,可以,但是有问题:
1.效率 2.需要自己加锁处理
加锁的目的是为了保证多个进程修改同一块数据时,同一时间只能有一个修改,即串行的修改,
没错,速度是慢了,牺牲了速度而保证了数据安全。
文件当做数据库,模拟抢票(Lock互斥锁)
5,进程间通信
(1)进程间通信(IPC)方式一:队列(推荐使用)
队列先进先出,栈后进先出
方法介绍:
q.put方法用以插入数据到队列中
put方法还有两个可选参数:blocked和timeout。
如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,
直到该队列有剩余的空间。
如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
q.get方法可以从队列读取并且删除一个元素。
get方法有两个可选参数:blocked和timeout。
如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,
会抛出Queue.Empty异常。
如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,
否则,如果队列为空,
则立即抛出Queue.Empty异常.
q.get_nowait():同q.get(False)
q.put_nowait():同q.put(False)
q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样
(2)进程间通信(IPC)方式二:管道
创建管道的类:
Pipe([duplex]):在进程之间创建一条管道,并返回元组(conn1,conn2),
其中conn1,conn2表示管道两端的连接对象,强调一点:必须在产生Process对象之前产生管道
参数介绍:
dumplex:默认管道是全双工的,如果将duplex射成False,conn1只能用于接收,conn2只能用于发送。
方法介绍:
主要方法:
conn1.recv():接收conn2.send(obj)发送的对象。如果没有消息可接收,recv方法会一直阻塞。
如果连接的另外一端已经关闭,那么recv方法会抛出EOFError。
conn1.send(obj):通过连接发送对象。obj是与序列化兼容的任意对象
conn1.close():关闭连接。如果conn1被垃圾回收,将自动调用此方法
conn1.fileno():返回连接使用的整数文件描述符
conn1.poll([timeout]):如果连接上的数据可用,返回True。timeout指定等待的最长时限。如果省略此参数,方法将立即返回结果。如果将timeout射成None,操作将无限期地等待数据到达。
conn1.recv_bytes([maxlength]):接收c.send_bytes()方法发送的一条完整的字节消息。maxlength指定要接收的最大字节数。如果进入的消息,超过了这个最大值,将引发IOError异常,并且在连接上无法进行进一步读取。如果连接的另外一端已经关闭,再也不存在任何数据,将引发EOFError异常。
conn.send_bytes(buffer [, offset [, size]]):通过连接发送字节数据缓冲区,buffer是支持缓冲区接口的任意对象,offset是缓冲区中的字节偏移量,而size是要发送字节数。结果数据以单条消息的形式发出,然后调用c.recv_bytes()函数进行接收
conn1.recv_bytes_into(buffer [, offset]):接收一条完整的字节消息,并把它保存在buffer对象中,该对象支持可写入的缓冲区接口(即bytearray对象或类似的对象)。offset指定缓冲区中放置消息处的字节位移。返回值是收到的字节数。如果消息长度大于可用的缓冲区空间,将引发BufferTooShort异常。

6,进程池
开多进程的目的是为了并发,如果有多核,通常有几个核就开几个进程,进程开启过多,效率反而会下降
(开启进程是需要占用系统资源的,而且开启多余核数目的进程也无法做到并行),但很明显需要并发执行的任务
要远大于核数,这时我们就可以通过维护一个进程池来控制进程数目,比如httpd的进程模式,规定最小进程数和最大进程数…
当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,
但如果是上百个,上千个目标,手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。
而且对于远程过程调用的高级应用程序而言,应该使用进程池,Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,就重用进程池中的进程。
在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。

#函数式进程
import multiprocessing,time
def run(name):
    time.sleep(5)
    print("来了",name)
if __name__ =="__main__":
    p1 = multiprocessing.Process(target=run,kwargs={"name":"老弟1"},name="l1")
    p2 = multiprocessing.Process(target=run,kwargs={"name":"老弟2"},name="l2")
    p3 = multiprocessing.Process(target=run,kwargs={"name":"老弟3"},name="l3")
    p1.daemon = True #设置守护进程
    p1.start()
    p2.start()
    p3.start()
    #p1.terminate() #强制终止进程p1
    #print(p1.is_alive())#测试是否活着
    #time.sleep(6)
    #print(p1.is_alive())
    p1.join(timeout=2)
    print("主进程结束")
#继承式进程
import multiprocessing,time
class MyProcess(multiprocessing.Process):
    def __init__(self):
        multiprocessing.Process.__init__(self)
    def run(self):
        print("来了,老弟")
if __name__ == "__main__":
    p1 = MyProcess()
    p2 = MyProcess()
    p1.start()
    p2.start()
#打开文件用多个进程读取并写入文件中
import multiprocessing,time
def work(name,lock):
    lock.acquire()
    with open("b.txt",mode="r",encoding="utf8") as f:
        c = int(f.read())
        print(c)
        c -= 1
        time.sleep(1)
        print(name,c)
        with open("b.txt", mode="w", encoding="utf8") as k:
            k.write(str(c))
    lock.release()
if __name__ == "__main__":
    lock = multiprocessing.Lock()
    p1 = multiprocessing.Process(target=work,args=("p1",lock))
    p2 = multiprocessing.Process(target=work,args=("p2",lock))
    p3 = multiprocessing.Process(target=work,args=("p3",lock))
    p1.start()
    p2.start()
    p3.start()

#进程中的队列
#队列通信
import multiprocessing
def work(q):
    if not q.empty():
        print(q.get())
if __name__ == "__main__":
    q = multiprocessing.Queue(maxsize=3)#maxsize是队列中允许最大项数,省略则无大小限制。
    q.put(1)
    q.put(2)
    q.put(3)
    p1 = multiprocessing.Process(target=work,args=(q,))
    p2 = multiprocessing.Process(target=work,args=(q,))
    p1.start()
    p2.start()

#管道通信(与队列的方式是类似的,队列就是管道加锁实现的):
import multiprocessing
#接收方============发送方
def jieshou(left,right):
    right.close()
    while True:
        print(left.recv())
        left.send("你好")
def fasong(left,right):
    left.close()
    while True:
        right.send("我是被发送出去的数据")
        print(right.recv())
if __name__ == '__main__':
    left,right = multiprocessing.Pipe()
    p1 = multiprocessing.Process(target=jieshou,args=(left,right))
    p2 = multiprocessing.Process(target=fasong,args=(left,right))
    p1.start()
    p2.start()

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值