进程--multiprocessing模块

进程–multiprocessing模块

一、知识梳理

1,进程

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。

2,线程

线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。

3,并发与并行

无论是并行还是并发,在用户看来都是’同时’运行的,不管是进程还是线程,都只是一个任务而已,真正在干活的是cpu,cpu来做这些任务,而一个cpu(单核)同一时刻只能执行一个任务

并发:看起来是同时运行。单个cpu+多道技术就可以实现并发,(并行也属于并发)
并行:任务同时在运行,只有cpu多核时才行

python的Gil特性下,根据任务种类的不同(io密集型、计算(cpu)密集型),选择使用多进程还是多线程。

4,同步与异步

同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不会返回。按照这个定义,其实绝大多数函数都是同步调用。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务

异步的概念和同步相对。当一个异步功能调用发出后,调用者不能立刻得到结果。当该异步功能完成后,通过状态、通知或回调来通知调用者。如果异步功能用状态来通知,那么调用者就需要每隔一定时间检查一次,效率就很低。如果是使用通知的方式,效率则很高,因为异步功能几乎不需要做额外的操作。至于回调函数,其实和通知没太多区别。

二、Process类的介绍

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

multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。

multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。

Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)

强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号

group: 线程组,目前还没有实现,库引用中提示必须是None;
target: 要执行的方法;
name: 进程名;
args/kwargs: 要传入方法的参数。

实例方法:

  is_alive():返回进程是否在运行。

  join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。

  start():进程准备就绪,等待CPU调度

  run():strat()调用run方法,如果实例进程时未指定传入target,这start执行默认run()方法。

  terminate():不管任务是否完成,立即停止工作进程

属性:

  daemon:和线程的setDeamon功能一样

  name:进程名字。

  pid:进程号。

p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)  

三、Process使用

#直接使用Process类生成子进程
from multiprocessing import Process
import os,time

def test(num):
    print(num)
    print(time.ctime())
    print(os.getpid())

if __name__ == '__main__':
    # p1.start()
    p = []
    for i in range(10):
        p1 = Process(target=test,args=(i,))
        p.append(p1)
    print(p)
    for i in p:
        i.start()

    for i in p:
        i.join()
    print (time.ctime ())
    print (os.getpid ())
    print('the end')    
[<Process(Process-11, initial)>, <Process(Process-12, initial)>, <Process(Process-13, initial)>, <Process(Process-14, initial)>, <Process(Process-15, initial)>, <Process(Process-16, initial)>, <Process(Process-17, initial)>, <Process(Process-18, initial)>, <Process(Process-19, initial)>, <Process(Process-20, initial)>]
Thu May 24 21:24:41 2018
4440
the end
#自定义类继承Process类
from multiprocessing import Process
import os,time
class Myprocess(Process):
    def __init__(self,num):
        super().__init__()
        self.num = num

    def run(self):
        print(self.num)
        print(time.ctime())
        print(os.getpid())

if __name__ == '__main__':
    p = []
    for i in range(10):
        p1 = Myprocess(i)
        p.append(p1)
    print(p)


    for i in p:
        i.start()

    for i in p:
        i.join()
    print (time.ctime ())
    print (os.getpid ())
    print('the end')
[<Myprocess(Myprocess-1, initial)>, <Myprocess(Myprocess-2, initial)>, <Myprocess(Myprocess-3, initial)>, <Myprocess(Myprocess-4, initial)>, <Myprocess(Myprocess-5, initial)>, <Myprocess(Myprocess-6, initial)>, <Myprocess(Myprocess-7, initial)>, <Myprocess(Myprocess-8, initial)>, <Myprocess(Myprocess-9, initial)>, <Myprocess(Myprocess-10, initial)>]
Thu May 24 21:24:18 2018
4440
the end

四、进程间通信

1,进程队列–Queue

from multiprocessing import Process,Queue
import os,time

q = Queue()

def putit(q):
    q.put('123')
    q.put('abc')
    q.put({'ab':12})
    print(os.getpid())

def getit():
    print(q.get())
    print(q.get())
    print(q.get())
    print (os.getpid ())

if __name__ == '__main__':
    p = Process(target=putit,args=(q,))
    print(p.name,p.pid)
    p.start()
    p.join()

    getit()

2,管道–Pipe

from multiprocessing import Process, Pipe
def f(conn):
    conn.send('hello master')
    res = conn.recv()
    print('回复信息:',res)
    conn.close()
    print('q2_id:',id(conn))

if __name__ == "__main__":

    parent_conn,child_conn = Pipe()
    print('q1_id:',id(child_conn))
    p = Process(target=f,args=(child_conn,))
    p.start()
    print(parent_conn.recv())
    parent_conn.send('hello saber')

3,Manager

Queue和pipe只是实现了数据交互,并没实现数据共享,即一个进程去更改另一个进程的数据。

from multiprocessing import Process, Manager

def f(d, l,n):

    d[n] = '1'    #{0:"1"}
    d['2'] = 2    #{0:"1","2":2}

    l.append(n)    #[0,1,2,3,4,   0,1,2,3,4,5,6,7,8,9]
    #print(l)


if __name__ == '__main__':

    with Manager() as manager:        #manager = Manager()

        d = manager.dict()#{}

        l = manager.list(range(5))#[0,1,2,3,4]


        p_list = []

        for i in range(10):
            p = Process(target=f, args=(d,l,i))
            p.start()
            p_list.append(p)

        for res in p_list:
            res.join()

        print(d)
        print(l)
{}
[0, 1, 2, 3, 4]

五、进程同步—同步锁

为了解决进程间互相抢占某一资源,比如输出,使用同步锁

from multiprocessing import Process,Lock
import time

def f(l,i):

    with l: 
        #l.aqquire()                 如果不用with
        print('hello %s'%i)
        time.sleep(1)
        #l.release()

if __name__ == '__main__':
    lock = Lock()

    for i in range(10):
        Process(target=f,args=(lock,i)).start()

六、进程池

进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。

进程池中有两个方法:

apply

apply_async

多进程是实现并发的手段之一,需要注意的问题是:

1、很明显需要并发执行的任务通常要远大于核数
2、一个操作系统不可能无限开启进程,通常有几个核就开几个进程
3、进程开启过多,效率反而会下降(开启进程是需要占用系统资源的,而且开启多余核数目的进程也无法做到并行)

例如当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个。。。手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。

from  multiprocessing import Process,Pool
import time,os

def Foo(i):

    time.sleep(1)
    print(i)
    print("son",os.getpid())

    return "HELLO %s"%i


def Bar(arg):
    print(arg)
    # print("hello")
    # print("Bar:",os.getpid())

if __name__ == '__main__':

    pool = Pool(5)
    print("main pid",os.getpid())
    for i in range(100):
        #pool.apply(func=Foo, args=(i,))  #同步接口
        #pool.apply_async(func=Foo, args=(i,))

        #回调函数:  就是某个动作或者函数执行成功后再去执行的函数

        pool.apply_async(func=Foo, args=(i,),callback=Bar)

    pool.close()
    pool.join()         # join与close调用顺序是固定的

    print('end')

七、协程

协程,又称微线程,纤程。英文名Coroutine。

优点1: 协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

优点2: 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

#yield实现
import time

def consumer(name):
    print('-----ready to eat-----')
    while 1:
        new_sushi = yield
        print('[%s] is eating sushi %s'%(name,new_sushi))
        time.sleep(1)

def producer():

    r = cook.__next__()
    r = cook2.__next__ ()
    n = 0
    while 1:
        time.sleep(1)
        print('[producer] is making sushi %s and %s'%(n,n+1))
        cook.send(n)
        cook.send(n+1)

        n +=2

if __name__ == '__main__':
    cook = consumer('consumer1')
    cook2 = consumer('consumer2')
    p = producer()

greedlet模块

greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator

from greenlet import greenlet

def test1():

    print(12)
    gr2.switch()
    print(34)
def test2():
    print(56)
    gr1.switch()
    print(78)
    gr1.switch()

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr2.switch()
Gevent模块
import gevent

import requests,time


start=time.time()

def f(url):
    print('GET: %s' % url)
    resp =requests.get(url)
    data = resp.text
    print('%d bytes received from %s.' % (len(data), url))

gevent.joinall([

        gevent.spawn(f, 'https://www.python.org/'),
        gevent.spawn(f, 'https://www.yahoo.com/'),
        gevent.spawn(f, 'https://www.baidu.com/'),
        gevent.spawn(f, 'https://www.sina.com.cn/'),

])



print("cost time:",time.time()-start)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值