zoukankan      html  css  js  c++  java
  • 进程,协程

    一:进程:进程是操作系统结构的基础;是一个正在执行的程序;计算机中正在运行的程序实例;可以分配给处理器并由处理器执行的一个实体;由单一顺序的执行显示,一个当前状态和一组相关的系统资源所描述的活动单元

    1.多进程

    由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。Python提供了非常好用的多进程包multiprocessing,只需要定义一个函数,Python会完成其他所有事情。借助这个包,可以轻松完成从单进程到并发执行的转换。multiprocessing支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。
    
      multiprocessing包是Python中的多进程管理包。与threading.Thread类似,它可以利用multiprocessing.Process对象来创建一个进程。该进程可以运行在Python程序内部编写的函数。该Process对象与Thread对象的用法相同,也有start(), run(), join()的方法。此外multiprocessing包中也有Lock/Event/Semaphore/Condition类 (这些对象可以像多线程那样,通过参数传递给各个进程),用以同步进程,其用法与threading包中的同名类一致。所以,multiprocessing的很大一部份与threading使用同一套API,只不过换到了多进程的情境。
    
    但在使用这些共享API的时候,我们要注意以下几点:
    
    在UNIX平台上,当某个进程终结之后,该进程需要被其父进程调用wait,否则进程成为僵尸进程(Zombie)。所以,有必要对每个Process对象调用join()方法 (实际上等同于wait)。对于多线程来说,由于只有一个进程,所以不存在此必要性。
    multiprocessing提供了threading包中没有的IPC(比如Pipe和Queue),效率上更高。应优先考虑Pipe和Queue,避免使用Lock/Event/Semaphore/Condition等同步方式 (因为它们占据的不是用户进程的资源)。
    多进程应该避免共享资源。在多线程中,我们可以比较容易地共享资源,比如使用全局变量或者传递参数。在多进程情况下,由于每个进程有自己独立的内存空间,以上方法并不合适。此时我们可以通过共享内存和Manager的方法来共享资源。但这样做提高了程序的复杂度,并因为同步的需要而降低了程序的效率。
    Process.PID中保存有PID,如果进程还没有start(),则PID为None。
    
    window系统下,需要注意的是要想启动一个子进程,必须加上那句if __name__ == "main",进程相关的要写在这句下面。

    来直接看一下代码

    from multiprocessing 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 = ("alex",))
            p_list.append(p)
            p.start()
        for i in p_list:
            p.join()                #注意这个地方得到join,等到上面的所有执行完以后再执行最后的end
        print("end")
    结果为:

    hello alex Wed Oct 12 16:50:18 2016
    hello alex Wed Oct 12 16:50:18 2016
    hello alex Wed Oct 12 16:50:18 2016
    end

    2.类式调用

    from multiprocessing import Process
    import time
    
    class MyProcess(Process):
        def __init__(self,):
            super(MyProcess,self).__init__()
            #self.name = name
        def run(self):
            time.sleep(1)
            print("hello",self.name,time.ctime())     #此时的self.name 是进程对象下的一个属性,是有名字的,
    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")
    结果为:

    hello MyProcess-1 Wed Oct 12 16:59:27 2016
    hello MyProcess-2 Wed Oct 12 16:59:27 2016
    hello MyProcess-3 Wed Oct 12 16:59:27 2016
    end

    3.进程关系(父进程与子进程之间的关系)

    from multiprocessing import Process
    import os
    import time
    def info(title):
        print(title)
        print("module name:",__name__)
        print("parent process:",os.getppid())
        print("process id:",os.getpid())
    def f(name):
        info("33[3;1mfunction f33[0m")
        print("hello",name)
    
    if __name__ == "__main__":
        info("33[32;1mmain process line33[0m")
        time.sleep(2)
        p = Process(target = info,args=("bob",))
        p.start()
        p.join()
    结果为:

    main process line
    module name: __main__
    parent process: 12888
    process id: 13432
    bob
    module name: __mp_main__
    parent process: 13432
    process id: 13036

    二:Process类

    构造方法:
    
    Process([group [, target [, name [, args [, kwargs]]]]])
    
      group: 线程组,目前还没有实现,库引用中提示必须是None; 
      target: 要执行的方法; 
      name: 进程名; 
      args/kwargs: 要传入方法的参数。
    
    实例方法:
    
      is_alive():返回进程是否在运行。
    
      join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。
    
      start():进程准备就绪,等待CPU调度
    
      run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。
    
      terminate():不管任务是否完成,立即停止工作进程
    
    属性:
    
      authkey
    
      daemon:和线程的setDeamon功能一样
    
      exitcode(进程在运行时为None、如果为–N,表示被信号N结束)
    
      name:进程名字。
    
      pid:进程号。

    三:进程间通讯:这里都是通过代码来看的

    3.1首先来看一个队列,进程队列queue

    from multiprocessing import Process, Queue
    
    def f(q,n):
        q.put([42, n, 'hello'])
    
    if __name__ == '__main__':
        q = Queue()
        p_list=[]
        for i in range(3):
            p = Process(target=f, args=(q,i))
            p_list.append(p)
            p.start()
        print(q.get())
        print(q.get())
        print(q.get())
        for i in p_list:
                i.join()

    3.2      pipe

    # import os
    #
    # from multiprocessing import Process, Pipe
    #
    # def f(conn):
    #     conn.send('约吗')
    #     print(conn.recv(),'in the %s'%os.getpid())
    #     conn.close()
    #
    # if __name__ == '__main__':
    #     parent_conn, child_conn = Pipe()
    #     p = Process(target=f, args=(child_conn,))
    #     p2 = Process(target=f, args=(child_conn,))
    #     p.start()
    #     p2.start()
    #     print(parent_conn.recv())  # prints "[42, None, 'hello']"
    #     print(parent_conn.recv())  # prints "[42, None, 'hello']"
    #     parent_conn.send('hello')
    #     parent_conn.send('hello2')
    #
    #     p.join()

    4.  manager         来实现一个数据共享

    from multiprocessing import Process, Manager
    
    def f(d,l,n):
        d[n] = '1'
        d['2'] = 2
        d[0.25] = None
        l.append(n)
        #print(l)
        print('sub',id(d))
    
    if __name__ == '__main__':
        with Manager() as manager:#  with open() as f==   f=open()       manager=Manager()
            d = manager.dict()
    
            l = manager.list(range(5))
            p_list = []
    
            print('main',id(d))
            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)

    四:进程池

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

    进程池中有两个方法:

    • apply
    • apply_async
    from  multiprocessing import Process, Pool
    import time
    import os
    
    def Foo(i):
        time.sleep(2)
        print('sub %s'%os.getpid())
    
        return i + 100
    
    
    def Bar(arg):
        print('Bar:',os.getpid())
        print('-->exec done:', arg)
    
    if __name__=='__main__':
        pool = Pool()
        print('main:',os.getpid())
        for i in range(10):
            pool.apply_async(func=Foo, args=(i,),callback=Bar)
            #pool.apply(func=Foo, args=(i,))
        print('end')
    
        pool.close()
        pool.join()

      第二部分:           协程

    协程
    
    协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程。
    
    协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:
    
    协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。
    
    
    协程的好处:
    
    无需线程上下文切换的开销
    无需原子操作锁定及同步的开销
    方便切换控制流,简化编程模型
    高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
    缺点:
    
    无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
    进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

    我们来看例子慢慢认识协程

    1.   yield支持下的协程         yield有一个并发的效果,在这里其实实现的就是一个协程

        首先我们先来简单的回顾一下yield,比如说

    def f():
        print("ok")
    f()                 # 这种情况下回正常执行函数打印出结果为OK,看下面
    def f():
        print("OK")
        yield
    f()                  # 这个时候是什么都没有,为什么,这里的yield是一个生成器
    print(f())        #<generator object f at 0x00000000006AC048>,那我们怎样去打印出上面的结果呢,看下面加的一些

    gen = f()
    next(gen)
    next(gen)        #这样我们就可以取到值                     注意:  gen.__next__()和next(gen)是等价的
    如果我们在yield下面加上这样
    print('ok1')
    yield 5
    print("OK2") 这样的话OK2也是不会打印的,因为此时yield已经折回了,当然要想打印出OK2,我们再来一个next就行,还需要一个yield
    yield 7

    最后面next(gen)这样就可以实现

    yield 后面是可以跟参数的,那么我们如何取到yield后面的参数值呢,因为返回的其实就是next(gen),所以呢,看下面这样就可以取到返回值
    ret = next(gen)
    print(ret) 此时取到的就是返回值
    我们除了next进入生成器对象里面我们还可以通过send进入,看下面例子
    count = yield 5
    print(count)
    x = gen.send(10)
    print(x)
    但是如果我们开始直接就send一个值是不行的,必须传入一个空值才行,就是gen.send(None)

    我这里写的有一点乱,可以看下面的代码在一步步看我的就而已理解了
    def f():
        print('ok1')
        count=yield 5
        print(count)
        print('ok2')
        yield 67
    
    #print(f())  #<generator object f at 0x00000000006AC048>
    
    gen=f()
    
    # ret=next(gen)
    # print(ret)
    
    next(gen)#gen.send(None)#
    
    x=gen.send(10)
    print(x)#67
    
    

    再来看一个例子

    import time
    import queue
    
    def consumer(name):
        print("--->starting ...")
        while True:
            new_baozi = yield
            print("[%s] is eating baozi %s" % (name, new_baozi))
            # time.sleep(1)
    
    def producer():
        next(con)
        next(con2)
        n = 0
        while n < 5:
            n += 1
            print("33[32;1m[producer]33[0m is making baozi %s" % n)
    
            con.send(n)
            con2.send(n)
    
    if __name__ == '__main__':
        con = consumer("c1")   #  创建一个生成器对象con
        con2 = consumer("c2")  #  创建一个生成器对象con2
        p = producer()         #  执行producer函数,p就是函数返回值

    2.Greenlet  下的协程

    from greenlet import greenlet
    def test1():
        print(12)
        gr2.switch()
        print(34)
        gr2.switch()
    def test2():
        print (56)
        gr1.switch()
        print (78)
    
    gr1 = greenlet(test1)
    gr2 = greenlet(test2)        #这里也是生成了一个对象
    print(gr1)
    print(gr2)
    结果为:

    <greenlet.greenlet object at 0x000000CD25A1FCC0>
    <greenlet.greenlet object at 0x000000CD25A1FDF0>
    12
    56
    34
    78

    3.gevent   下 支持 的 协程

    Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

    首先我们要装一个gevent,在file下找到setting下的这个选项就可以在里面添加各种你所需要的配置

    来看一个例子

    # import gevent
    # import time
    #
    # def foo():
    #     print('Running in foo',time.ctime())
    #     gevent.sleep(1)                                 #这里有同学可能会问为什么不用time,如果用time的话这就是一个串行的过程,就不会切换了,
    #     print('Explicit context switch to foo again',time.ctime())
    #
    #
    # def bar():
    #     print('Explicit context to bar',time.ctime())
    #     gevent.sleep(2)
    #     print('Implicit context switch back to bar',time.ctime())
    #
    # gevent.joinall([
    #     gevent.spawn(foo),
    #     gevent.spawn(bar),
    # ])

    结果为:

    runing in foo Fri Oct 14 17:18:38 2016
    explicit context to bar Fri Oct 14 17:18:38 2016
    explicit context switch to foo again Fri Oct 14 17:18:39 2016
    implicit context switch back to bar Fri Oct 14 17:18:40 2016

    再来看一个爬虫的例子,比如说校花网

    #爬虫
    # from gevent import monkey
    # monkey.patch_all()              #最大程度的利用IO阻塞
    # import gevent
    # from urllib.request import urlopen
    # import time
    # def f(url):
    #     print("GET: %s" % url)
    #     resp = urlopen(url)
    #     data = resp.read()
    #     with open("xiaohuawang.html","wb")as f:
    #         f.write(data)
    #     print("%d bytes received from %s." % (len(data),url))
    # #l = ["http://www.python.org/","http://www.yahoo.com/","http://github.com/"]
    # start = time.time()
    # for url in l:
    #     f(url)                   #是等价于下面的,
    # gevent.joinall([
    #     gevent.spawn(f,"http://www.xiaohuar.com/"),
    #     # gevent.spawn(f,"http://www.yahoo.com/"),
    #     # gevent.spawn(f,"http://github.com/"),
    # ])
    # print(time.time() - start)
    #
  • 相关阅读:
    前台js的复制与粘贴
    idea
    前台 js easyUI datagrid 杂记 验证(disable)
    《命运赋》
    前台
    js 、 java去除字符串中的数字
    【 协议 】 freemodbus的分层结构分析
    王爽 汇编11.10(2)编程用串传送指令,将F000H段中最后的16个字节复制到data段中
    王爽 汇编11.10(1)编程用串传送指令,将data段中的第一个字符串赋值到它后面的空间中
    汇编语搜索言中32位CPU多出的两个FS、GS段寄存器,全称是什么啊?
  • 原文地址:https://www.cnblogs.com/mars527/p/5953988.html
Copyright © 2011-2022 走看看