多进程模块 multiprocessing,process类,进程队列、管道以及Managers,进程同步,协程

Multiprocessing is a package that supports spawning processes using an API similar to the threading module. The multiprocessing package offers both local and remote concurrency,effectively side-stepping the Global Interpreter Lock by using subprocesses instead of threads. Due to this, the multiprocessing module allows the programmer to fully leverage multiple processors on a given machine. It runs on both Unix and Windows.

由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。

multiprocessing包是Python中的多进程管理包。与threading.Thread类似,它可以利用multiprocessing.Process对象来创建一个进程。该进程可以运行在Python程序内部编写的函数。该Process对象与Thread对象的用法相同,也有start(), run(), join()的方法。此外multiprocessing包中也有Lock/Event/Semaphore/Condition类 (这些对象可以像多线程那样,通过参数传递给各个进程),用以同步进程,其用法与threading包中的同名类一致。所以,multiprocessing的很大一部份与threading使用同一套API,只不过换到了多进程的情境。

通过生成进程实例来调用:

from multiprocessing import Process
#import Process
import time
def f(name):
    time.sleep(1)
    print('hello', name,time.ctime())

if __name__ == '__main__':
    p_list=[]
    for i in range(3):
        p = Process(target=f, args=('alvin',))
        #p=multiprocessing.Process(target=f, args=('alvin',))
        p_list.append(p)
        p.start()
    for i in p_list:
        p.join()
    print('end')

通过继承改写调用:

from multiprocessing import Process
import time

class MyProcess(Process):#自定义函数来继承Process
    def __init__(self):
        super(MyProcess, self).__init__()
        #self.name = name

    def run(self):#改写父类方法
        time.sleep(1)
        print ('hello', self.name,time.ctime())


if __name__ == '__main__':
    p_list=[]
    for i in range(3):
        p = MyProcess()
        p.start()
        p_list.append(p)

    for p in p_list:
        p.join()

    print('end')

process类:

import time
from  multiprocessing import Process

def foo(i):
    time.sleep(1)
    print (p.is_alive(),i,p.pid)
    time.sleep(1)

if __name__ == '__main__':
    p_list=[]
    for i in range(10):
        p = Process(target=foo, args=(i,))
        #p.daemon=True
        p_list.append(p)

    for p in p_list:
        p.start()
    # for p in p_list:
    #     p.join()

    print('main process end')

构造方法:

Process([group [, target [, name [, args [, kwargs]]]]])

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

实例方法:

扫描二维码关注公众号,回复: 3462566 查看本文章

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

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

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

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

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

属性:

  daemon:和线程的setDeamon功能一样

  name:进程名字。

  pid:进程号。

进程对列Queue


import queue,time

import multiprocessing
def foo(q):
    time.sleep(1)
    print("son process",id(q))
    q.put(123)
    q.put("yuan")

if __name__ == '__main__':
    #q=queue.Queue()
    q=multiprocessing.Queue()
    p=multiprocessing.Process(target=foo,args=(q,))
    p.start()
    #p.join()
    print("main process",id(q))
    print(q.get())
    print(q.get())

管道

The Pipe() function returns a pair of connection objects connected by a pipe which by default is duplex (two-way)

from multiprocessing import Process, Pipe
def f(conn):
    conn.send([12, {"name":"yuan"}, 'hello'])
    response=conn.recv()
    print("response",response)
    conn.close()
    print("q_ID2:",id(conn))

if __name__ == '__main__':

    parent_conn, child_conn = Pipe() #双向管道  Pipe一个类()直接产生两个结果,可发可收

    print("q_ID1:",id(child_conn))
    p = Process(target=f, args=(child_conn,))
    p.start()

    print(parent_conn.recv())   # prints "[42, None, 'hello']"
    parent_conn.send("儿子你好!")
    p.join()

Managers

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

Managers 支持类型listdictNamespaceLockRLockSemaphoreBoundedSemaphoreConditionEventBarrierQueueValue and Array

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:
        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)

进程同步:如果没有这样会让多个进程同时抢占同一个资源。造成串行等,加把锁一个一个来

from multiprocessing import Process, Lock


def f(l, i):
    with l:
        print('hello world %s' % i)
# l.acquire()   ##不用with可以用这种方式
# print('hello world %s' % i)
# l.release()
if __name__ == '__main__':
    lock = Lock()

    for num in range(10):
        Process(target=f, args=(lock, num)).star

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

进程池中有两个方法:apply  同步     apply_asym异步

from  multiprocessing import Process,Pool
import time,os

def Foo(i):

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

    return "HELLO %s"%i
    #这里的return值就是回调函数后面的参数

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调用顺序是固定的,close()必须放在join()后面

    print('end')

                                                      协程

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

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

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

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

yield的简单实现

import time
import queue

def consumer(name):
    print("--->ready to eat baozi...")
    while True:
        new_baozi = yield
        print("[%s] is eating baozi %s" % (name,new_baozi))
        #time.sleep(1)

def producer():

    r = con.__next__()
    r = con2.__next__()
    n = 0
    while 1:
        time.sleep(1)
        print("\033[32;1m[producer]\033[0m is making baozi %s and %s" %(n,n+1) )
        con.send(n)
        con2.send(n+1)

        n +=2


if __name__ == '__main__':
    con = consumer("c1")
    con2 = consumer("c2")
    p = producer()

consumer()就不是函数了,就成生成器对象了。到yield就停止,后面代码不执行。send值后完成信息传递,再执行

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()#切换启动

requests和gevent完成小爬虫

import gevent
import requests,time
# 模块(爬虫)#模块(爬虫)
start=time.time()
def f(url):
    print('GET: %s' % url)#url即网址
    resp =requests.get(url)#爬
    with open('abc.html','w',encoding='utf-8') as f :
        resp=f.write()
    data = resp.text#打印
    print('%d bytes received from %s.' % (len(data), url))

# f('https://www.python.org/')
# f('https://www.yahoo.com/')    #串行
# f('https://www.baidu.com/')
# f('https://www.sina.com.cn/')
# f("http://www.xiaohuar.com/hua/")

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/'),
        gevent.spawn(f, 'http://www.xiaohuar.com/hua/'),
])

print("cost time:",time.time()-start)

猜你喜欢

转载自blog.csdn.net/weixin_42166745/article/details/82557887