Python并发编程(1)--进程

进程与线程概念


进程:一个运行的程序。给定的时间内只做一件事情。
每个进程都有独立的系统状态(内存,已打开文件列表,程序计数器,保存函数局部变量的调用栈)
线程:和进程类似,但所有线程都运行在一个进程里,共享运行环境
进程间通信:基于消息传递,通过管道或套接字
竞争条件:多个任务同时更新一个数据结构,会导致数据不一致
全局解释锁(GIL):Python虚拟机被GIL控制来保证同时刻只有一个线程在运行,即使有多处理器。Python虚拟机执行顺序:1.设置GIL 2.切换一个线程 3.运行 4.设置睡眠状态 5.解锁 6.重复上述
I/O密集型程序(调用内置的操作系统C代码的程序),GIL会在调用它之前释放,允许其他线程在等待I/O操作时候运行。所以多线程一般用于I/O密集型程序。
计算密集型程序用线程则会在等待中等浪费时间。
同时,如果有需要要大量线程来工作,会有系统资源,上下文切换,锁定等问题。
一般计算密集型程序,使用C扩展或multiprocessing模块。
C扩展能够释放锁定,不与解释器交互。
multiprocessing模块可将工作分派给不受锁定限制的单独子进程。
异步事件:中心时间循环监控所有I/O,并将异步事件分发给大量I/O处理器

multiprocessing模块


进程 创建启动函数:

# -*- coding: UTF-8 -*-
import multiprocessing
import time
def clock(interval)
    while True:
            print("The time is %s" % time.ctime())
            time.sleep(self.interval)
p = multiprocessing.Process(target=clock,args(5,))
p.start()

定义为继承Process的类:

import multiprocessing
import time
class ClockProcess(multiprocessing.Process):
    def __init__(self,interval):
        #初始化Process
        multiprocessing.Process.__init__(self)
        self.interval = interval
    #重新实现run()函数
    def run(self):
        while True:
            print("The time is %s" % time.ctime())
            time.sleep(self.interval)
if __name__ == '__main__':
    p = ClockProcess(5)
    p.start()

进程间通信

Queue

示例代码:

# -*- coding: UTF-8 -*-
import multiprocessing
import time
def consumer(input_q):
    while True:
        item = input_q.get()
        if item == None:
            break
        #处理项目
        print(item)
        print("The time is %s" % time.ctime())
        #发出信号任务完成
        input_q.task_done()
def producer(sequence,output_q):
    for item in sequence:
        #将项目放入队列
        output_q.put(item)

#建立进程
if __name__ == '__main__':
    q = multiprocessing.JoinableQueue()
    #运行消费者进程
    cons_p1 = multiprocessing.Process(target=consumer,args=(q,))
    cons_p1.daemon=True
    cons_p1.start()
    sequence = [1,2,3,4]

    cons_p2 = multiprocessing.Process(target=consumer,args=(q,))
    cons_p2.daemon=True
    cons_p2.start()
    sequence.append(5)
    #sequence = [1,2,3,4]
    producer(sequence,q)
    #也可发送信号 当做标志
    q.put(None)
    #producer(sequence,q)
    #等待所有被处理完
    q.join()

q.put(item[,block[,timeout]])
将item放入队列,如果队列已满,将阻塞直到有空间为止。
block控制阻塞行为,默认为True。如果为False,
将引发Queue,Empty异常。
timeout指定在阻塞模式中等待可用空间的时长。超时后引发Queue.Full异常。
一般把每个项目序列化,通过管道或者套接字发送给进程。
规则:数量少大对象

Pipe

Pipe([duplex])在进程间创建管道,返回管道两端的Connection对象。
管道实例:

# -*- coding: UTF-8 -*-
import multiprocessing
import time
'''
'''
def consumer(pipe):
    output_p,input_p = pipe
    input_p.close()#关闭输入端
    while True:
        try:
            item = output_p.recv()
             #recv()接受send()返回的对象,如果另一端关闭,没有数据
            #则引发EOFError异常,使用异常处理可以避免recv()挂起
        except EOFError:
            break
        #处理项目
        #也可以加上诸如:
        #output_p.send()来发送消息
        #变成双向通信
        print(item)
    print("The time is %s" % time.ctime())
def producer(sequence,input_p):
    for item in sequence:
        #将项目放入管道
        input_p.send(item)
        #也可以加上诸如:
        #input_p.recv()来接受output_p的消息
        #变成双向通信

#建立进程
if __name__ == '__main__':
    (output_p,input_p) = multiprocessing.Pipe()
    #运行消费者进程
    cons_p = multiprocessing.Process(target=consumer,args=((output_p,input_p),))
    cons_p.start()
    #不用输出管道-关闭输出管道
    output_p.close()
    sequence = [1,2,3,4]
    producer(sequence,input_p)
    #关闭输入管道表示完成
    input_p.close()
    #等待所有被处理完
    cons_p.join()

注意:管道端口没有使用应该关闭它,只有两端关闭才会引发EOFError异常,避免recv()挂起。

pool

进程池可以把数据处理任务放到Pool。有点类似于列表解析。如映射-规约。
pool提供numprocess 的进程数,如果达到最大值就会等待,直到某个进程结束才创建新的进程。
可以利用可迭代对象分发重复任务成进程,丢给Pool自动处理多个进程,然后再收集任务结果返回给生成器,这样相比单个进程更高效。
特别是对于多核系统,当然这样也增加了额外的通讯开销。

Pool([numprocess [,initializer[, initargs]]])
#numprocess 创建的进程数,
#initializer进程启动要执行的可调用对象,
#initargs要传递给initializer的参数元组。
'''
python
'''
# -*- coding: UTF-8 -*-
import os
import multiprocessing
import hashlib
'''
p.imap(func,iterable [,chunksize])
map()函数版本,返回可迭代对象
p.imap_unordered(func,iterable [,chunksize])
进程接受结束时,返回结果次序任意
'''
BUFSIZE = 8192
POOLSIZE = 2

def compute_digest(filename):
    try:
        f = open(filename,"rb")
    except IOError:
        return None
    digest = hashlib.sha512()
    while True:
        chunk = f.read(BUFSIZE)
        if not chunk:
            break
        digest.update(chunk)
    f.close()
    return filename,digest.digest()

def build_digest_map(topdir):
    digest_pool = multiprocessing.Pool(POOLSIZE)
    allfiles = (os.path.join(path,name)
                for path,dirs,files in os.walk(topdir)
                    for name in files)

    digest_map = dict(digest_pool.imap_unordered(compute_digest,allfiles,20))
    digest_pool.close()
    return digest_map

if __name__ == '__main__':
    digest_map = build_digest_map("/home/hb/PycharmProjects/pachong")
    for (k,v) in digest_map.items():
        print('key=',k,'value=',v)
    print(len(digest_map))

多进程总结

  • 确保进程之间传递的所有数据都能够序列化
  • 尽可能使用消息队列
  • 单进程的函数内部,不要使用全局变量,而应该显示的传递参数
  • 显示地关闭进程,多注意异常处理,防止一些奇怪的事情让整个程序崩溃
  • 尽量让事情变得简单
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值