博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
协程, IO阻塞模型 和 IO非阻塞模型
阅读量:5248 次
发布时间:2019-06-14

本文共 4799 字,大约阅读时间需要 15 分钟。

(1):小知识点:                                    查找线程的线程号     from threading import  current_thread      ====>current_thread()  可以查到自己的线程号  

线程池中的回调函数是子线程调用的,和父线程没有关系

进程池中的回调函数是父进程调用的,和子进程没有关系      ,注意是父进程

一.协程:        ======> 是一个更加轻量级的单位,是组成线程的各个函数,由程序员去调度的.(注意:pyhton中底层都是用yield实现的)

                                   说白了就是通过代码手动实现在单线程内遇到IO就让出CPU,引导切换别的函数执行,以此实现并发,提高单线程效率.

********yield 只能实现单纯的切换函数和保存函数状态的功能******* 不能实现:当某一个函数遇到io阻塞时,自动的切换到另一个函数去执行**********

因为Cpython中有GIL锁,限制了同一时间点,只能执行一个线程,为了充分利用cpu的利用率,在单线程内,实现并发的效果,就出现了协程

然而实现并发的效果(切换+保存状态),就是要在单线程内解决多个任务函数如果遇到IO,马上自动切换到其他的任务函数去执行任务

二,  <1>greenlet模块      ===> 能简单实现函数与函数之间的切换, 但是遇到IO操作,不能自动切换到其他函数(一个函数执行一个函数)

    (1) 注册一下函数func,将函数注册成一个对象f1

       f1 = greenlet(func)

    (2) 调用func,使用f1.switch(),如果func需要传参,就在switch这里传即可

示例:   time.sleep(1)不影响函数的交叉实现

from greenlet import greenletimport time# greenlet 只是可以实现一个简单的切换功能,还是不能做到遇到IO就切换# g1 = greenlet(func)   实例化一个对象# g1.switch()  用这种方式去调用func函数# 当使用switch调用func的时候,什么时候func会停止运行?#   1 要么return      2 要么在func内部又遇到 switchdef eat(name):    print('%s吃炸鸡'%name)    time.sleep(2)    f2.switch('小雪2')    print('%s吃雪糕'%name)    f2.switch()def drink(name):    print('%s喝啤酒'%name)    f1.switch()    print('%s喝可乐'%name)f1 = greenlet(eat)f2 = greenlet(drink)f1.switch('小雪')
View Code

 <2>gevent模块:可以实现在某函数内部遇到IO操作,就自动的切换到其他函数内部去执行  

     g = gevent.spawn(func,参数) 注册一下函数func,返回一个对象g   这里有个坑;那个函数先注册就先执行

     gevent.join(g) #等待g指向的函数func执行完毕,如果在执行过程中,遇到IO,就切换

     gevent.joinall([g1,g2,g3])#等待g1 g2 g3指向的函数func执行完毕

示例:实现智能化的遇见IO就去自动切换的功能       (注意;结合猴子,实现并发的同时,各个函数的变量的状态也能随着改变保持住)

#<1>实现了遇到IO就切换的功能,但是只能识别自身的IOimport geventimport timedef func():    print('1')    gevent.sleep(1)    # time.sleep(1)    print('2')def func1():    print('3')    # gevent.sleep(1)    time.sleep(1)         #geven不能识别其他的IO操作,只能认识自己的IO操作,所以会继续向下执行函数    print('4')p2 = gevent.spawn(func1) #先开启哪个协程就先执行哪个函数,函数中遇到不认识的IO会畅通无阻执行下去p1 = gevent.spawn(func)p1.join()#<2>实现了遇到IO就切换的功能,除了识别自身的IO,还能识别大部分常用的IO操作from gevent import monkeymonkey.patch_all()# 可以让gevent识别大部分常用的IO操作import timedef func1():    print('1 2 3 4')    time.sleep(1)    print('3 2 3 4')    # gevent.sleep(1)def func2():    print('2 2 3 4')    time.sleep(1)    print('再来一次')g1 = gevent.spawn(func1)g2 = gevent.spawn(func2)g1.join()# 等待g1指向的任务执行结束g2.join()
View Code

三,selet  和 poll  和 epoll的区别 ====>windows只能用selet     但是lunix三个都能使用

  select和poll有一个共同的机制,都是采用轮训的方式去询问内核,有没有数据准备好了

  select有一个最大监听事件的限制,32位机限制1024个事件,,6位机限制2048个事件

  poll没有,理论上poll可以开启无限大,1G内存大概够你开10W个事件去监听

  epoll是最好的,采用的是回调机制,解决了select和poll共同存在的问题,而且epoll理论上也可以开启无限多个监听事件

四, (1)基于selet的网络IO模型

import selectimport socketsk = socket.socket()sk.bind(('127.0.0.1',8080))sk.listen()del_l = []rlist = [sk]# 是用来让select帮忙监听的 所有 接口# select:windows/linux是监听事件有没有数据到来# poll:  linux   也可以做select的工作# epoll: linux   也可以做类似的工作while 1:    r,w,x = select.select(rlist,[],[])# 传参给select,当rlist列表中哪个接口有反应,就返回给r这个列表    if r:        for i in r:# 循环遍历r,看看有反应的接口到底是sk  还是conn            if i == sk:                # 如果是sk,那就表示有客户端的连接请求                '''sk有数据要接收,代表着有客户端要来连接'''                conn,addr = i.accept()                rlist.append(conn)# 把新的客户端的连接,添加到rlist,继续让select帮忙监听            else:                # 如果是conn,就表示有客户端给我发数据了                '''conn有数据要接收,代表要使用recv'''                try:                    msg_r = i.recv(1024).decode('utf-8')                    if not msg_r:                        '''客户端执行了close,客户端主动正常关闭连接'''                        del_l.append(i)                        i.close()                    else:                        print(msg_r)                        i.send(msg_r.upper().encode('utf-8'))                except ConnectionResetError:                    pass        if del_l:# 删除那些主动断开连接的客户端的conn            for conn in del_l:                rlist.remove(conn)            del_l.clear()
View Code

  (2)用非阻塞IO模型解决阻塞IO

import socketsk = socket.socket()sk.setblocking(False)sk.bind(('127.0.0.1',8080))sk.listen()l = []del_l = []while 1:    try:        conn,addr = sk.accept()# 如果是阻塞IO模型,在这里程序会一直等待。        l.append(conn)# 将每个请求连接的客户端的conn添加到列表中    except BlockingIOError:        for conn in l:# 去遍历所有客户端的conn,看看有没有客户端给我发送数据了            try:                info = conn.recv(1024).decode('utf-8')# 尝试接收,看看有没有客户端给我发数据                if not info:# 如果客户端正常执行了close,服务器会接收到一个空                    del_l.append(conn)# 将已经结束的客户端的conn,添加到要删除的列表中                    print('客户端正常退出了!')                    conn.close()# 因为客户端已经主动close,所以服务器端的conn也要close                else:                    print(info)                    conn.send(info.upper().encode('utf-8'))            except BlockingIOError:                continue# 是没有接受到客户端发来的数据而报错            except ConnectionResetError:                pass# 是因为客户端强制退出而报错        if del_l:            for conn in del_l:                l.remove(conn)            del_l = []# 在删除完主动关闭的客户端的连接之后,应该把此列表清空,否则报错
View Code

 

转载于:https://www.cnblogs.com/laogao123/p/9550547.html

你可能感兴趣的文章
linux后台运行和关闭SSH运行,查看后台任务
查看>>
桥接模式-Bridge(Java实现)
查看>>
303. Range Sum Query - Immutable
查看>>
C# Dynamic通用反序列化Json类型并遍历属性比较
查看>>
前台freemark获取后台的值
查看>>
Leetcode: Unique Binary Search Trees II
查看>>
C++ FFLIB 之FFDB: 使用 Mysql&Sqlite 实现CRUD
查看>>
Spring-hibernate整合
查看>>
c++ map
查看>>
exit和return的区别
查看>>
Django 相关
查看>>
比较安全的获取站点更目录
查看>>
空间分析开源库GEOS
查看>>
前端各种mate积累
查看>>
Python(软件目录结构规范)
查看>>
Windows多线程入门のCreateThread与_beginthreadex本质区别(转)
查看>>
Nginx配置文件(nginx.conf)配置详解1
查看>>
linux php编译安装
查看>>
redis哨兵集群、docker入门
查看>>
hihoCoder 1233 : Boxes(盒子)
查看>>