• python之进程和线程3


    1 multiprocessing模块

    (1.)直接导入
    from multiprocessing import Process
    import os
    import time
    def info(name):
        print("name:",name)
        print('parent process:', os.getppid())
        print('process id:', os.getpid())
        print("------------------")
    def foo(name):
        info(name)
        time.sleep(50)
    if __name__ == '__main__':
        info('main process line')
        p1 = Process(target=info, args=('alvin',))
        p2 = Process(target=foo, args=('egon',))
        p1.start()
        p2.start()
    
        p1.join()
        p2.join()
    
        print("ending")
    time.sleep(100)
    >>
    name: main process line
    parent process: 16976
    process id: 18456
    ------------------
    name: alvin
    parent process: 18456
    process id: 19884
    ------------------
    name: egon
    parent process: 18456
    process id: 19112
    ------------------
    ending
    

      

    (2.)创建类的方法

    构造方法:

    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():不管任务是否完成,立即停止工作进程

    属性:

      daemon:和线程的setDeamon功能一样

      name:进程名字。

      pid:进程号。

    2 协程

    协程的优点:

    (1)     由于单线程不存在切换

    (2)     不再有任何锁的概念

    yield是最基本的携程函数
    没有办法监听到IO,进行切换
    可以保存到数据的状态通过send方法来运行
    import time
    # 注意到consumer函数是一个generator(生成器):
    # 任何包含yield关键字的函数都会自动成为生成器(generator)对象
    
    def consumer():
        r = ''
        while True:
            n = yield r
            if not n:
                return
            print('[CONSUMER] ←← Consuming %s...' % n)
            time.sleep(1)
            r = '200 OK'
    def produce(c):
        # 1、首先调用c.next()启动生成器
        next(c)
        n = 0
        while n < 5:
            n = n + 1
            print('[PRODUCER] →→ Producing %s...' % n)
            # 2、然后,一旦生产了东西,通过c.send(n)切换到consumer执行;
            cr = c.send(n)
            # 4、produce拿到consumer处理的结果,继续生产下一条消息;
            print('[PRODUCER] Consumer return: %s' % cr)
        # 5、produce决定不生产了,通过c.close()关闭consumer,整个过程结束。
        c.close()
    if __name__=='__main__':
        # 6、整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。
        c = consumer()
        produce(c)
    

      

    greenlet模块

    可以实现手动切换

    调用属性swich

    gevent可以实现IO的监听

    gevent.joinall 开启所有程序

    gevent.spawn 切换

     

    3 IO模型

    IO指input, output

    IO发生时涉及的对象和步骤

    会涉及到两个系统对象,一个是调用这个IO的process(or thread),另一个就是系统内核(kernel)。当一个操作发生时,会经历两个阶段:

    (1)     等待数据准备

    (2)     将数据从内核拷贝到进程中

    IO模型类型:

    1. 1.      阻塞  IO

    1. 1.      非阻塞 IO

    非阻塞IO:发送多次系统调用

    优点:wait for data无阻塞

    缺点:系统调用太多

              不能及时拿到数据

    两个阶段:wait for data非阻塞

                   copy data 阻塞

    非阻塞的recvform系统调用调用之后,进程并没有被阻塞,内核马上返回给进程,如果数据还没准备好,此时会返回一个error。进程在返回之后,可以干点别的事情,然后再发起recvform系统调用。重复上面的过程,循环往复的进行recvform系统调用。这个过程通常被称之为轮询。轮询检查内核数据,直到数据准备好,再拷贝数据到进程,进行数据处理。需要注意,拷贝数据整个过程,进程仍然是属于阻塞的状态。

    1. 1.      IO多路复用(监听多个链接)

    特点:(1)全程阻塞

    能监听多个文件描述符  实现并发

    #服务端
    
    import select
    
    import socket
    
    sock=socket.socket()#产生一个套接字
    
    sock.bind(("127.0.0.1",8080))
    
    sock.listen(5)
    
    sock.setblocking(False)
    
    inputs=[sock,]
    
    while 1:
    
        r,w,e=select.select(inputs,[],[])#监听有变化的套接字sock
    
        #wait for data
    
        for obj in r:
    
            if obj==sock:
    
                conn,addr=obj.accept()#从内核copy信息到用户态
    
                print("conn",conn)
    
                inputs.append(conn)#监听列表添加客户conn
    
            else:
    
                data=obj.recv(1024)#接收信息
    
                print(data.decode("utf8"))
    
                send_data=input(">>")#发送信息
    
                obj.send(send_data.encode("utf8"))
    
    #客户端
    
    import socket
    
    sock=socket.socket()
    
    sock.connect(("127.0.0.1",8080))
    
    while 1:
    
        data=input("input>>")
    
        sock.send(data.encode("utf8"))
    
        recv_data=sock.recv(1024)
    
        print(recv_data.decode("utf8"))
    
     
    
    sock.close()
    

      

    对于文件描述符(套接字对象)

    (1)    是一个非零整数,不会变

    (2)    收发数据的时候,对于接收端而言,数据先到内核空间,然后copy到用户空间,同时,内核空间数据清除

    1. 1.      异步IO

    全程无阻塞

    5.驱动信号

    小结:

    有阻塞blocking

    无阻塞non-blocking

    调用blocking IO会一直block住对应的进程知道操作完成

    non-blocking IO在kernel还准备数据的情况下会立刻返回

    有阻塞是同步阻塞:阻塞  非阻塞  IO多路复用

    无阻塞是异步阻塞:异步IO

    4 selectors模块

    IO多路复用实现机制

    Win:select

    Linux:select,poll,epoll

    Select缺点:1.每次调用select都要将所有的fd(文件描述符)拷贝到内核空间,导致效率下降

               2.遍历所有的fd,是否有数据访问(最重要的问题)

               3.最大连接数(1024)

    poll:最大连接数没有限制

    epoll:1.第一个函数创建epoll句柄,将所有的fd(文件描述符)拷贝到内核空间

            只需要拷贝一次

          2.回调函数:某一个函数或者某一个动作成功完成之后会触发的函数

            为所有的fd绑定一个回调函数,但有数据访问触发该回调函数

            回调函数将fd放到列表中

    import selectors
    import socket
    sock=socket.socket()
    sock.bind(("127.0.0.1",8080))
    sock.listen(5)
    sock.setblocking(False)
    sel=selectors.DefaultSelector()#根据具体平台选择最佳IO多路机制
    def read(conn,mask):
        try:
            data=conn.recv(1024)
            print(data.decode("utf8"))
            data2=input(">>")
            conn.send(data2.encode("utf8"))
        except Exception:
            sel.unregister(conn)
    def accept(sock,mask):
        sel.register(sock,selectors.EVENT_READ,accept)
        conn,addr=sock.accept()
        sel.register(conn,selectors.EVENT_READ,read)
    sel.register(sock,selectors.EVENT_READ,accept)#注册功能
    while 1:
        events=sel.select()
        for key,mask in events:
            print(key.data)#定义的函数
            print(key.fileobj)#socket对象
            func=key.data
            obj=key.fileobj
            func(obj,mask)
    
    break
    
    
    import socket
    sock=socket.socket()
    sock.connect(("127.0.0.1",8080))
    while 1:
        data=input("input>>")
        sock.send(data.encode("utf8"))
        recv_data=sock.recv(1024)
        print(recv_data.decode("utf8"))
    
    sock.close()
    

      

    5. 队列

    队列用在多线程,多进程中,用来保护数据

    队列是个数据类型

    优点:线程安全

    import queue
    q=queue.Queue(3)#默认是先进先出
    q.put(111)
    q.put("hello")
    q.put(222)
    print(q.get())
    print(q.get())
    print(q.get())
    >>
    111
    hello
    222
    
    import queue
    q=queue.Queue(3)#默认是先进先出
    q.put(111)
    q.put("hello")
    q.put(222)
    q.put(223,False)#q=queue.Queue(3)队列定义只能放3个值,
    # #超过限额时,返回错误信息
    print(q.get())
    print(q.get())
    print(q.get())
    
    q.get()#没有数据的时候不会报错,只会等待
    q.get(False)#数据为空,报错
    
    先进后出
    import queue
    q=queue.LifoQueue()
    q.put(111)
    q.put(5)
    q.put(43)
    print(q.get())
    
    优先级
    import queue
    q=queue.PriorityQueue()
    q.put([4,"hello"])
    q.put([1,"hello5"])
    print(q.get())
    

      

  • 相关阅读:
    Android BroadcastReceiver 接收收到短信的广播
    Android ContenObserver 监听联系人数据变化
    Android 通过AIDL在两个APP之间Service通信
    Android studio安装和问题
    Android 底部导航栏实现一 Fragment-replace
    Android ImgView属性
    Android TextView常用属性
    Android颜色配置器
    Android gravity和layout_gravity的区别
    Android 组件的三种点击事件写法
  • 原文地址:https://www.cnblogs.com/asaka/p/6847006.html
Copyright © 2020-2023  润新知