• Python_oldboy_自动化运维之路_线程,进程,协程(十一)


    本节内容:

    1. 线程
    2. 进程
    3. 协程
    4. IO多路复用
    5. 自定义异步非阻塞的框架

    线程和进程的介绍

    举个例子,拿甄嬛传举列线程和进程的关系:

    总结:
    1.工作最小单元是线程,进程说白了就是提供资源的

    2.一个应用程序至少有一个进程,一个进程里至少有一个线程

    3.应用场景:io密集型适合用多线程,计算密集型(cpu)适合用多进程

    4.GIL:全局解释器锁,作用:保证同一个进程中只能有一个线程同时被调用

    5.python的一个诟病:前提是被cpu调度,因为有GIL,一个应用只有一个进程,纵容有多个线程,也体现不出多核的优势,除非有多个进程,好处是降低的python开发者的难度。

    1.线程

    Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元,开线程的主要的作用是可以帮我们同时做某些事情。

    线程的基本使用:

    1.线程的基本使用

    # -*- coding: UTF-8 -*-
    #blog:http://www.cnblogs.com/linux-chenyang/
    import threading
    import time
    
    def task(arg):                                      #隔1s后执行这三十个线程,是并发的,同时工作
        time.sleep(1)
        print('aaaaaaa',arg)
    
    
    for i in range(30):                                 #这个是创建出30个线程来
        t = threading.Thread(target=task,args=[i,])
    #    t.setDaemon(True)     #主程序结束,不等待子线程
    #    t.setDaemon(False)   #默认
    
        t.start()
    
    #    t.join()    #等一个线程执行完在执行下一个线程
    #    t.join(1)  #等待最大时间,最多等1秒
    
    print('end')    #这个就相当于主线程
    线程
    #end是主线程,等主线程创建好后会隔1s并发执行30个函数
    end
    aaaaaaa 0
    aaaaaaa 1
    aaaaaaa 2
    aaaaaaa 4
    aaaaaaa 5
    aaaaaaa 3
    aaaaaaa 6
    aaaaaaa 7
    aaaaaaa 9
    aaaaaaa 8
    aaaaaaa 11
    aaaaaaa 10
    aaaaaaa 13
    aaaaaaa 12
    aaaaaaa 14
    aaaaaaa 17
    aaaaaaa 15
    aaaaaaa 16
    aaaaaaa 19
    aaaaaaa 18
    aaaaaaa 20
    aaaaaaa 21
    aaaaaaa 22
    aaaaaaa 23
    aaaaaaa 25
    aaaaaaa 26
    aaaaaaa 24
    aaaaaaa 27
    aaaaaaa 28
    aaaaaaa 29
    输出

    2.线程任务被调用过程

    #线程执行的过程,自定义线程,我们的函数是被run方法调用的
    import threading
    import time
    
    class MyThread(threading.Thread):
        def __init__(self,func,*args,**kwargs):
            super(MyThread,self).__init__(*args,**kwargs)
            self.func = func
    
        def run(self):
            self.func()
            print('-------首先执行的是run方法-----')
    
    def task():
        print('task函数。。。。')
    
    
    obj=MyThread(func=task)
    obj.start()
    线程
    task函数。。。。
    -------首先执行的是run方法-----
    输出

    线程锁:

    3.线程锁,只能有一个人使用锁

    #用处:这样的话变量v就是来一个减一个,不会出现假如其中有线程没有执行完,在去减就会出错
    
    import threading
    import time
    
    #创建锁
    lock = threading.Lock()
    #lock = threading.RLock()   #使用这种方法可以锁多次,但是解锁也要解锁多次,带递归的锁
    
    v=10
    
    def task(arg):
        time.sleep(2)
    
        #申请使用锁,其他人等
        lock.acquire()
        #lock.acquire()
    
        global v
        v -= 1
        print(v)
    
        #释放
        lock.release()
        #lock.release()
    
    for i in range(10):
        t = threading.Thread(target=task,args=(i,))  #元组的方式最好加逗号,(后续)怕误以为是个函数
        t.start()
    线程锁
    9
    8
    7
    6
    5
    4
    3
    2
    1
    0
    输出

    4.线程锁,同时多个人使用锁

    import threading
    import time
    
    #创建锁
    lock = threading.BoundedSemaphore(3)      #同时锁三个线程
    
    v=10
    
    def task(arg):
    
        lock.acquire()
    
        time.sleep(1)                        #当进来锁后等1秒,这时显示打印会三条三条的显示。
        global v
        v -= 1
        print(v)
    
        lock.release()
    
    
    for i in range(10):
        t = threading.Thread(target=task,args=(i,))  #元组的方式最好加逗号,(后续)怕误以为是个函数
        t.start()
    线程锁
    #三个三个显示,等1秒
    9
    8
    7
    6
    5
    4
    3
    2
    1
    0
    输出

    5.线程锁,事件锁的应用

    #3.事件锁,当触发某个条件,我生成的子线程才工作
    import threading
    import time
    
    #创建锁
    lock = threading.Event()
    
    def task(arg):
    
        time.sleep(1)
        #锁住所有的线程
        lock.wait()
        print(arg)
    
    for i in range(10):
        t = threading.Thread(target=task,args=(i,))
        t.start()
    
    while True:
        value = input('>>')
        if value == '1':
            lock.set()
    #        lock.clear()   默认
    事件锁
    >>1
    >>0
    2
    4
    6
    8
    1
    3
    5
    7
    9
    输出

    6.线程锁,条件condition用法

    #4.条件,Condition的用法,输入几就释放几个线程
    import threading
    import time
    
    #创建锁
    lock = threading.Condition()
    
    def task(arg):
    
        time.sleep(1)
        #锁住所有的线程
        lock.acquire()
        lock.wait()
        print('线程',arg)
        lock.release()
    
    for i in range(10):
        t = threading.Thread(target=task,args=(i,))
        t.start()
    
    while True:
        value = input('>>')
        lock.acquire()
        lock.notify(int(value))
        lock.release()
    线程锁
    C:Python35python3.exe D:/pycharm/s16/day9/2.锁.py
    >>2
    >>线程 0
    线程 2
    3
    >>线程 1
    线程 4
    线程 3
    输出

     线程池(先创建连接,在去执行):1.最基本的使用,线程池,若有100个任务,我的线程池总共有5个线程,每个线程处理完任务会继续处理下一个任务。

    # -*- coding: UTF-8 -*-
    #blog:http://www.cnblogs.com/linux-chenyang/
    from concurrent.futures import ThreadPoolExecutor
    import time
    
    def task(arg):
        time.sleep(1)
        print('线程池',arg)
    
    pool = ThreadPoolExecutor(5)
    
    for i in range(100):
        #去连接池中取连接
        pool.submit(task,i)
    线程池
    #任务会5个5个来,每隔1秒
    C:Python35python3.exe D:/pycharm/s16/day9/3.线程连接池.py
    线程池 0
    线程池 2
    线程池 1
    线程池 3
    线程池 4
    线程池 5
    线程池 6
    线程池 7
    线程池 8
    线程池 9
    线程池 10
    线程池 11
    线程池 13
    线程池 12
    线程池 14
    线程池 15
    线程池 18
    线程池 16
    线程池 17
    线程池 19
    线程池 20
    线程池 21
    线程池 22
    线程池 23
    线程池 24
    线程池 25
    线程池 26
    线程池 27
    线程池 28
    线程池 29
    线程池 30
    线程池 32
    线程池 34
    线程池 31
    线程池 33
    线程池 35
    线程池 36
    线程池 37
    线程池 38
    线程池 39
    线程池 40
    线程池 41
    线程池 42
    线程池 44
    线程池 43
    线程池 45
    线程池 46
    线程池 47
    线程池 48
    线程池 49
    线程池 50
    线程池 51
    线程池 52
    线程池 53
    线程池 54
    线程池 55
    线程池 56
    线程池 57
    线程池 58
    线程池 59
    线程池 60
    线程池 61
    线程池 62
    线程池 63
    线程池 64
    线程池 65
    线程池 66
    线程池 67
    线程池 68
    线程池 69
    线程池 70
    线程池 71
    线程池 72
    线程池 74
    线程池 73
    线程池 75
    线程池 76
    线程池 77
    线程池 79
    线程池 78
    线程池 80
    线程池 81
    线程池 82
    线程池 84
    线程池 83
    线程池 85
    线程池 87
    线程池 86
    线程池 88
    线程池 89
    线程池 91
    线程池 90
    线程池 92
    线程池 93
    线程池 94
    线程池 95
    线程池 96
    线程池 97
    线程池 98
    线程池 99
    输出

    2.并发发送http请求,获取结果

    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    import time
    # pip3 install requests
    import requests
    
    # ########## 1. 并发发送Http请求,获取结果 ##########
    
    def task(url):
        response = requests.get(url)
        print('得到结果:',url,len(response.content))
        # ....
    
    pool = ThreadPoolExecutor(2)
    url_list = [
        'http://www.oldboyedu.com',
        'http://www.autohome.com.cn',
        'http://www.baidu.com',
    ]
    for url in url_list:
        print('开始请求',url)
        # 去连接池中获取链接
        pool.submit(task,url)
    线程池

    3.连接池可以并发去登陆多台主机,去执行命令。

    from concurrent.futures import ThreadPoolExecutor
    import time
    # pip3 install requests
    import requests
    
    def task(host):
        import paramiko
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(hostname=host, port=22, username='wupeiqi', password='123')
        stdin, stdout, stderr = ssh.exec_command('df')
        result = stdout.read()
        ssh.close()
        print(result)
    
    pool = ThreadPoolExecutor(2)
    host_list = [
        'c1.com',
        'c2.com',
        'c3.com',
    ]
    for host in host_list:
        print('开始请求',host)
        # 去连接池中获取链接
        pool.submit(task,host)
    线程锁

    创建线程池的思维:若要自定义线程池,线程池里面的线程默认是没有启动线程的,来一个任务起一个线程,来第二个任务先看下第一个线程空闲不空闲,要是不空闲在起第二个线程,以此类推,直到达到最大线程数的值。

    4.回调函数

    #线程池之分布式执行,回调执行
    #假如我拿到了各个网站的内容,有的人把内容存到本地,有的人把内容上次到网盘
    #我们利用线程池只把数据的结果拿到,至于用户怎么操作,那就让他自己写个方法(函数)
    
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    import requests
    
    #我自己的方法
    def text(future):
        download_response = future.result()                #6.若要拿到response的内容,就执行future.result()语法
        print('我自己的保存数据的方法。。。。',download_response.url,download_response.status_code)
    
    
    def download(url):                                     #4.下载网页的方法
        response = requests.get(url)
    #    print('得到结果:',url,len(response.content))
        return response    #response包含了下载的所有内容
    
    
    pool = ThreadPoolExecutor(2)                           #1.先有个线程池
    
                                                           #2.有个各个网站的列表
    url_list = [
        'http://www.oldboyedu.com',
        'http://www.autohome.com.cn',
        'http://www.baidu.com',
    ]
    for url in url_list:
        print('开始请求',url)
        # 去连接池中获取链接
        future=pool.submit(download,url)                  #3.用线程下载网站的内容
    
        #这就是回调函数:执行完后在调用一个函数
        future.add_done_callback(text)                    #5.获取到网站内容后执行text的方法
    回调函数
    开始请求 http://www.oldboyedu.com
    开始请求 http://www.autohome.com.cn
    开始请求 http://www.baidu.com
    我自己的保存数据的方法。。。。 http://www.oldboyedu.com/ 200
    我自己的保存数据的方法。。。。 http://www.baidu.com/ 200
    我自己的保存数据的方法。。。。 http://www.autohome.com.cn/beijing/ 200
    输出

    5.回调函数优化,把线程和回调函数写成一个公共的模块,用户传人网站内容和方法

    # -*- coding: UTF-8 -*-
    #blog:http://www.cnblogs.com/linux-chenyang/
    from concurrent.futures import ThreadPoolExecutor
    import requests
    
    
    def download(url):
        response = requests.get(url)
        return response  #response包含了下载网站的所有内容
    
    
    # url_list = [
    #     {'url':'http://www.oldboyedu.com','call':'f1'},
    #     {'url':'http://www.autohome.com.cn','call':'f1'},
    #     {'url':'http://www.baidu.com','call':'f1'},
    # ]
    
    def run(url_list):
        pool = ThreadPoolExecutor(2)
        for item in url_list:
            url = item['url']
            call = item['call']
    
            future=pool.submit(download,url)
            future.add_done_callback(call)
    nb_thread
    # -*- coding: UTF-8 -*-
    #blog:http://www.cnblogs.com/linux-chenyang/
    
    import nb_thread
    
    def f1(future):
        response =  future.result()
        print(response.text)                   #网站的内容以文本的格式显示
    
    def f2(future):
        response =  future.result()
    
    def f3(future):
        response =  future.result()
    
    
    url_list = [
        {'url':'http://www.oldboyedu.com','call':f1},
        {'url':'http://www.autohome.com.cn','call':f2},
        {'url':'http://www.baidu.com','call':f3},
    ]
    
    nb_thread.run(url_list)
    用户

    2.进程

    线程和线程之间资源是共享的,进程和进程之间的资源是不共享的。

    进程的基本使用:

     1.进程和线程的套路插不多

    # -*- coding: UTF-8 -*-
    #blog:http://www.cnblogs.com/linux-chenyang/
    
    from multiprocessing import Process
    import time
    
    def task(arg):
        time.sleep(2)
        print(arg)
    
    if __name__ == '__main__':                  #windows上必须这么写,linux和mac上不用
        for i in range(10):
            p = Process(target=task,args=(i,))
    #        p.daemon=True   #主进程不等子进程执行完
            p.start()
    #        p.join(1)       #主进程最多等1秒
        print('主进程中的主线程执行到最后。。。。。')
    进程
    主进程中的主线程执行到最后。。。。。
    1
    2
    0
    3
    5
    7
    4
    6
    8
    9
    输出

    2.进程的数据是不共享的,验证如下,和线程做对比

    #2.验证进程数据不共享
    #查看进程输出,每次只有一个数据,不会记录上次的数据
    from  multiprocessing import Process
    
    def task(num,li):
        li.append(num)
        print(li)
    
    if __name__ == '__main__':
        v=[]
        for i in range(10):
            p = Process(target=task,args=(i,v))
            p.start()
    
    #利用线程,会记录上次的数据
    # import threading
    #
    # def task(num,li):
    #     li.append(num)
    #     print(li)
    #
    # if __name__ == '__main__':
    #     v=[]
    #     for i in range(10):
    #         p = threading.Thread(target=task,args=(i,v))
    #         p.start()
    进程数据不共享
    C:Python35python3.exe D:/pycharm/s16/day9/进程的基本使用.py
    [1]
    [4]
    [0]
    [3]
    [8]
    [9]
    [2]
    [7]
    [6]
    [5]
    输出

    3.进程之间实现共享数据的两种方法(备注:第二种方法不加join会报错,请忽略,原因为没有关闭连接,所以加join不会报错)

    特殊的东西
                 - Array(‘类型’,长度)
                 - Manager().list() / Manager().dict()

    # -*- coding: UTF-8 -*-
    #blog:http://www.cnblogs.com/linux-chenyang/
    #实现进程之间的共享
    # #方法一:基于c语言
    # from multiprocessing import  Process,Array
    #
    # def task(num,li):
    #     li[num]=1
    #     print(list(li))
    #
    # if __name__ == '__main__':
    #     v = Array('i',10)         #i是数据类型(数字),10表示长度,在c语言中列表必须制定数据类型和长度大小,不像python
    # #    print(v)       [0,0,0,0,0,0,0,0,0]
    #     for i in range(10):
    #         p = Process(target=task,args=(i,v,))
    #         p.start()
    #输出:
    # [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    # [1, 0, 1, 0, 0, 0, 0, 0, 0, 0]
    # [1, 0, 1, 0, 0, 1, 0, 0, 0, 0]
    # [1, 1, 1, 0, 0, 1, 0, 0, 0, 0]
    # [1, 1, 1, 0, 0, 1, 0, 0, 0, 1]
    # [1, 1, 1, 0, 0, 1, 0, 0, 1, 1]
    # [1, 1, 1, 0, 0, 1, 1, 0, 1, 1]
    # [1, 1, 1, 1, 0, 1, 1, 0, 1, 1]
    # [1, 1, 1, 1, 0, 1, 1, 1, 1, 1]
    # [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    
    #方法二:基于socket实现的
    from multiprocessing import  Process,Manager
    def task(num,li):
        li.append(num)
        print(li)
    
    if __name__ == '__main__':
        v = Manager().list()         #表示特殊的列表,可以共享
        for i in range(10):
            p = Process(target=task,args=(i,v,))
            p.start()
            p.join(1)
    进程共享
    C:Python35python3.exe D:/pycharm/s16/day9/5.进程之间的数据共享.py
    [0]
    [0, 1]
    [0, 1, 2]
    [0, 1, 2, 3]
    [0, 1, 2, 3, 4]
    [0, 1, 2, 3, 4, 5]
    [0, 1, 2, 3, 4, 5, 6]
    [0, 1, 2, 3, 4, 5, 6, 7]
    [0, 1, 2, 3, 4, 5, 6, 7, 8]
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    输出

    4.进程池,和线程池一样

    # -*- coding: UTF-8 -*-
    #blog:http://www.cnblogs.com/linux-chenyang/
    from concurrent.futures import ProcessPoolExecutor
    
    
    def call(arg):
        data = arg.result()
        print(data)
    
    def task(arg):
        print(arg)
        return arg+100
    
    if __name__ == '__main__':
        pool = ProcessPoolExecutor(5)
        for i in range(10):
            obj=pool.submit(task,i)
            obj.add_done_callback(call)
    线程池

    3.协程

    线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。

    协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

    协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;

    协程永远是一个线程在执行,对线程的一个分片处理,目的是充分利用线程的资源,单独拿出来用没啥作用,只有遇到IO才会有用greenlet:

    # -*- coding: UTF-8 -*-
    #blog:http://www.cnblogs.com/linux-chenyang/
    #greenlet模块需要先下载pip3 install 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)
    gr1.switch()
    greenlet(协程)
    C:Python35python3.exe D:/pycharm/s16/day9/7.协程.py
    12
    56
    34
    78
    输出

    gevent:

    根据协程二次开发:IO+线程,实现一个线程分片处理多个任务,充分利用资源

    #根据协程二次开发:IO+线程,实现一个线程分片处理多个任务,充分利用资源
    #gevent需要下载
    from gevent import monkey; monkey.patch_all()
    import gevent
    import requests
    
    def f(url):
        response = requests.get(url)
        print(response.url,response.status_code)
    
    gevent.joinall([
            gevent.spawn(f, 'http://www.qq.com/'),
            gevent.spawn(f, 'http://www.oldboyedu.com/'),
            gevent.spawn(f, 'http://www.baidu.com/'),
    ])
    IO+线程(基于greenletl)
    C:Python35python3.exe D:/pycharm/s16/day9/7.协程.py
    http://www.baidu.com/ 200
    http://www.oldboyedu.com/ 200
    http://www.qq.com/ 200
    输出

    4.IO多路复用

    http://www.cnblogs.com/wupeiqi/articles/5040823.html

    一个线程做到了同时监听多个端口发来的请求,伪造了一种并发的概念,所以的web框架都是基于以下代码演变过来的

    1.io多路复用(可以监听多个socket对象)

    import socket
    import select
    
    # IO多路复用:8002,8001
    #
    
    sk1 = socket.socket()
    sk1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
    sk1.bind(('127.0.0.1',8003,))
    sk1.listen(5)
    
    sk2 = socket.socket()
    sk2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
    sk2.bind(('127.0.0.1',8004,))
    sk2.listen(5)
    inputs = [sk1,sk2,]
    while True:
        # IO多路复用,同时监听多个socket对象
        #    - select,内部进行循环操作(1024)  在系统底层主动查看有没有人给我发来数据,谁发生变化我就拿来用给r,windows上只有select
        #    - poll, 内部进行循环操作         主动查看,和select区别是没有限制,select最大数据1024
        #    - epoll,                        被动告知(边缘触发),通过异步回调实现
        r,w,e = select.select(inputs,[],[],0.05)
        # r = [sk2,]    假如有人访问8002
        # r = [sk1,]    假如有人访问8001
        # r = [sk1,sk2] 假如同时访问8002和8001
        # r = []        #0.05是超时时间,假如没有人访问就是r就是空列表
        # r = [conn,]
        # r = [sk1,Wconn]
        #######
        for obj in r:                                   #r包含所有的对象,sk1,sk2,sk1.conn,sk2.conn
            if obj in [sk1,sk2]:
                # 新连接捡来了...
                print('新连接来了:',obj)
                conn,addr = obj.accept()                #conn这里有连接来了才会往下执行,否则会卡在等待连接
                inputs.append(conn)
            else:
                # 有连接用户发送消息来了..
                print('有用户发送数据了:',obj)
                data = obj.recv(1024)                   #建立完连接后客户端发数据了才会往下走,否则会一直等着收数据
                obj.sendall(data)
    io多路复用服务端
    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1',8003,))
    
    while True:
        v = input('>>>')
        client.sendall(bytes(v,encoding='utf-8'))
        ret = client.recv(1024)
        print('服务器返回:',ret)
    io多路复用客户端1
    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1',8004,))
    
    while True:
        v = input('>>>')
        client.sendall(bytes(v,encoding='utf-8'))
        ret = client.recv(1024)
        print('服务器返回:',ret)
    io多路复用客户端2
    #服务端
    C:Python35python3.exe D:/pycharm/s16/day9/8.IO多路复用.py
    新连接来了: <socket.socket fd=264, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8003)>
    有用户发送数据了: <socket.socket fd=272, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8003), raddr=('127.0.0.1', 58840)>
    新连接来了: <socket.socket fd=268, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8004)>
    有用户发送数据了: <socket.socket fd=276, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8004), raddr=('127.0.0.1', 58845)>
    
    #客户端1
    C:Python35python3.exe D:/pycharm/s16/day9/9.socket客户端.py
    >>>1
    服务器返回: b'1'
    >>>
    
    
    #客户端2
    C:Python35python3.exe D:/pycharm/s16/day9/10.socket客户端.py
    >>>2
    服务器返回: b'2'
    >>>
    输出

    2.io多路复用优化

    # -*- coding: UTF-8 -*-
    #blog:http://www.cnblogs.com/linux-chenyang/
    #r里面只接受,w发送
    
    import socket
    import select
    
    # IO多路复用:8002,8001
    #
    
    sk1 = socket.socket()
    sk1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
    sk1.bind(('127.0.0.1',8003,))
    sk1.listen(5)
    
    sk2 = socket.socket()
    sk2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
    sk2.bind(('127.0.0.1',8004,))
    sk2.listen(5)
    inputs = [sk1,sk2,]
    w_inputs = []
    while True:
        # IO多路复用,同时监听多个socket对象
        #    - select,内部进行循环操作(1024)  在系统底层主动查看有没有人给我发来数据,谁发生变化我就拿来用给r,windows上只有select
        #    - poll, 内部进行循环操作         主动查看,和select区别是没有限制,select最大数据1024
        #    - epoll,                        被动告知(边缘触发),通过异步回调实现
        r,w,e = select.select(inputs,w_inputs,inputs,0.05)
        # r = [sk2,]    假如有人访问8002
        # r = [sk1,]    假如有人访问8001
        # r = [sk1,sk2] 假如同时访问8002和8001
        # r = []        #0.05是超时时间,假如没有人访问就是r就是空列表
        # r = [conn,]
        # r = [sk1,Wconn]
        #######
        for obj in r:                                   #r包含所有的对象,sk1,sk2,sk1.conn,sk2.conn
            if obj in [sk1,sk2]:
                # 新连接捡来了...
                print('新连接来了:',obj)
                conn,addr = obj.accept()                #conn这里有连接来了才会往下执行,否则会卡在等待连接
                inputs.append(conn)
            else:
                # 有连接用户发送消息来了..
                print('有用户发送数据了:',obj)
                try:
                    data = obj.recv(1024)                   #建立完连接后客户端发数据了才会往下走,否则会一直等着收数据
                except Exception as ex:
                    data=''
                if data:                                    #假如收到了数据,就在发送
                    #obj.sendall(data)
                    w_inputs.append(obj)
                else:
                    obj.close()
                    inputs.remove(obj)
                    w_inputs.remove(obj)
    
        for obj in w:
            obj.sendall(b'ok')
            w_inputs.remove(obj)
    优化

    3.模拟sockserver

    import socket
    import select
    import threading
    
    # IO多路复用:8002,8001
    #
    ############################### 基于select实现服务端的“伪”并发 ###############################
    """
    def process_request(conn):
        while True:
            v = conn.recv(1024)
            conn.sendall(b'1111')
    
    sk1 = socket.socket()
    sk1.bind(('127.0.0.1',8001,))
    sk1.listen(5)
    inputs=[sk1,]
    while True:
        # IO多路复用,同时监听多个socket对象
        #    - select,内部进行循环操作(1024)  主动查看
        #    - poll, 内部进行循环操作         主动查看
        #    - epoll,                        被动告知
        r,w,e = select.select(inputs,[],inputs,0.05)
    
        for obj in r:
            if obj in sk1:
                # conn客户端的socket
                conn,addr = obj.accept()
                t = threading.Thread(target=process_request,args=(conn,))
                t.start()
    """
    # import socketserver
    #
    # class MyHandler(socketserver.BaseRequestHandler):
    #     def handle(self):
    #         pass
    #
    #
    # server = socketserver.ThreadingTCPServer(('127.0.0.1',8001),MyHandler)
    # server.serve_forever()
    模拟

     

    greenlet
  • 相关阅读:
    ASP.NET 2.0 web.config数据库连接设置与读取
    一句话影评
    百度地图api示例
    centos5.8 误改/etc/fstab后导致系统进不去 解决办法
    Nginx Gzip 压缩配置
    数据库设计的三大范式
    CentOS 6.0下vncserver安装配置
    MySQL配置文件my.cnf设置
    Linux下zip加密压缩
    keepalived的log
  • 原文地址:https://www.cnblogs.com/linux-chenyang/p/6601323.html
Copyright © 2020-2023  润新知