• 【0919 | Day 35】线程queue/线程池和进程池/协程


    socket多线程

    客户端

    import socket
    from threading import Thread
    
    def client_demo():
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        
        client.connect(('192.168.11.199', 8010))
        while True:
            msg = f'{currentThread().name}'
            if len(msg) == 0: break
            client.send(msg.encode('utf-8'))
            feedback = client.recv(1024)
            print(feedback.decode('utf-8'))
         
        client.close()
        
    if __name__ == '__main__':
        for i in range(5):
            t = Thread(target=client_demo)
            t.start()
                
    

    服务端

    import socket
    from threading import Thread
    
    def talk(conn):
        while True:
            try:
                msg = conn.recv(1024)
                if len(msg) == 0: break
                conn.send(msg.upper())
            except connectionResetError
            	print('客户端关闭了一个链接')
                break
        conn.close()
        
        
    def serve_demo():
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.bind(('192.168.11.199', 8010))
        server.listen(5)
        
        while True:
            conn, addr = server.accept()
            t = Thread(target=talk, args(conn,))
            t.start()
            
    if __name__ == '__main__':
        server_demo()
    

    线程queue

    用法一:先进先出(Queue)

    import queue
    
    q = queue.Queue()
    q.put('123')
    q.put('qweqwe')
    print(q.get())
    print(q.get())
    q.task_done()
    q.task_done()
    

    123
    qweqwe

    用法二:先进后出(LifoQueue)

    堆栈

    import queue
    
    q = queue.LifoQueue() 
    q.put('粉红色的背心儿')
    q.put('粉红色的裤子')
    q.put('欧文的各种设备')
    print(q.get())
    print(q.get())
    print(q.get())
    

    欧文的各种设备
    粉红色的裤子
    粉红色的背心儿

    用法三:优先级(PriorityQueue)

    通常这个元组的第一个值是int类型

    import queue
    
    # 可以根据优先级取数据
    q = queue.PriorityQueue() 
    q.put((50, '吴磊'))
    q.put((80, '陈飞宇'))
    q.put((1, '欧阳娜娜'))
    print(q.get())
    print(q.get())
    print(q.get())
    

    (1, '欧阳娜娜')
    (50, '吴磊')
    (80, '陈飞宇')

    线程定时器

    介绍

    启动某个线程的倒计时

    from threading import Thread
    import time
    
    def task():
        print('线程执行!')
        time.sleep(1)
        print('线程结束!')
        
    
    t = Timer(3, task) #3秒后执行task
    t.start()
    

    ==》时间过去3秒

    线程执行了

    ==》时间过去2秒

    线程结束了

    进程池和线程池

    介绍

    • 池:限制进程数或线程数
    • 限制时间:当并发的任务数量远远大于计算机所能承受的范围,即无法一次性开启过多的任务数量

    例子(进程池)

    from concurrent.future import ProcessPoolExecutor, ThreadPoolExecutor
    from threading import currentThread
    from multiprocessing import current_process
    import time
    
    def task(i):
        print(f'{current_process().name}在执行任务{i}')
        time.sleep(1)
        return i**2
    
    if __name__ == '__main__':
        pool = ProcessPoolExecutor(3) # 池子里只有3个进程
        fu_list = []
        for i in range(9):
            future = pool.submit(task, i) # task任务要做9次, 3个进程负责做这个事
            fu_list.append(future)
        pool.shutdown() # 关闭了池的入口,会等待所有的任务执行完,结束阻塞
        for fu in fu_list:
            print(fu.result())
    

    进程 SpawnProcess-1 在执行任务 0
    进程 SpawnProcess-2 在执行任务 1
    进程 SpawnProcess-3 在执行任务 2

    进程 SpawnProcess-1 在执行任务 3
    进程 SpawnProcess-2 在执行任务 4
    进程 SpawnProcess-3 在执行任务 5

    进程 SpawnProcess-1 在执行任务 6
    进程 SpawnProcess-2 在执行任务 7
    进程 SpawnProcess-3 在执行任务 8

    0
    1
    4
    9
    16
    25
    36
    49
    64

    例子(线程池)

    from concurrent.future import ProcessPoolExecutor, ThreadPoolExecutor
    from threading import currentThread
    from multiprocessing import current_process
    import time
    
    def task(i):
        print(f'{currentThread().name}在执行任务{i}')
        time.sleep(1)
        return i**2
    
    if __name__ == '__main__':
        pool = ThreadPoolExecutor(3) # 池子里只有3个线程
        fu_list = []
        for i in range(9):
            future = pool.submit(task, i) # task任务要做9次, 3个线程负责做这个事
            fu_list.append(future)
        pool.shutdown() # 关闭了池的入口,会等待所有的任务执行完,结束阻塞
        for fu in fu_list:
            print(fu.result())
    

    ThreadPoolExecutor-0_0 在执行任务 0
    ThreadPoolExecutor-0_1 在执行任务 1
    ThreadPoolExecutor-0_2 在执行任务 2

    ThreadPoolExecutor-0_2 在执行任务 3
    ThreadPoolExecutor-0_0 在执行任务 4
    ThreadPoolExecutor-0_1 在执行任务 5

    ThreadPoolExecutor-0_0 在执行任务 6
    ThreadPoolExecutor-0_1 在执行任务 7
    ThreadPoolExecutor-0_2 在执行任务 8

    0
    1
    4
    9
    16
    25
    36
    49
    64

    同步和异步

    介绍

    • 同步:提交了一个任务,必须等任务执行完拿到返回值,才能执行下一行代码(一般不使用)
    • 异步:提交了一个任务,不等执行完,就可以直接执行下一行代码

    协程

    介绍

    什么是协程?

    • 单线程下实现并发

    为什么要有协程?

    • 协程本质是程序员抽象出来的概念,操作系统根本不知道协程的存在

      • 操作系统 == 佛祖

      • 线程 == 孙悟空

      • 协程 == 猴毛

    • 通俗的说,孙悟空的一切活动都在佛祖的股掌之中。一旦某个小猴子遇到io或者磨磨蹭蹭耽误太长时间了,就需要交出自己的CPU执行权限(金箍棒),由别的猴子(线程)去做

    • 可是这样,孙悟空觉得不服气,凭什么???我也不想等那么长时间的io啊!因此,孙悟空一气之下,拔了一撮脑袋上的猴毛(协程),这些腾空出世的小猴子由他调遣,所以这下佛祖就没辙啦!人一多,办事效率就高了嘛

    什么样的协程有意义?

    • 只有遇到io切换才有意义

    协程的优点是什么?

    • 应用程序控制切换要比操作系统切换快得多(或者说协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,更加轻量级)
    • 单线程内就可以实现并发的效果,最大限度地利用CPU

    携程的缺点是什么?

    • 协程的本质是在单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程开启多个线程,每个线程开启协程
    • 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

    例子

    情况一:单纯地切换反而会降低运行效率

    import time
    
    def consumer(res):
        '''任务1:接收数据,处理数据'''
        pass
    
    def producer():
        '''任务2:生产数据'''
        res = []
        for i in range(10000000):
            res.append(i)
        return res
    
    start = time.time()
    
    #串行执行
    res = producer()
    consumer(res) #写成consumer(producer())会降低执行效率
    stop = time.time()
    print(stoop - start)
    
    
    #基于yield并发执行
    import time 
    def consumer():
        '''任务1:接收数据,处理数据'''
        while True:
            x = yield
    
    def producer():
        '''任务2:生产数据'''
        g = consumer()
        next(g)
        for i in range(10000000)
            g.send(i)
            
    start = time.time()
    
    #基于yield保存状态,实现两个任务直接来回切换,即并发的效果
    #PS:如果每个任务中都加上打印,那么明显地看到两个任务的打印是你一次我一次,即并发执行的
    
    producer()
    
    stop = time.time()
    print(stop - start)
    

    情况二:yield不能实现io切换

    import time 
    
    def consumer():
        '''任务1:接收数据, 处理数据'''
        while True:
            x = yield
            
    def producer():
        '''任务2:生产数据'''
        g = consumer()
        next(g)
        for i in range(10000000):
            g.send(i)
            time.sleep(2)
            
    start = time.time()
    producer() #并发执行,但是任务producer遇到io就会阻塞住,并不会切到该线程内的其他任务去执行
    
    stop = time.time()
    print(stop - start)
    

    gevent模块

    介绍

    • 实现并发同步或异步编程

    例子

    情况一:gevent

    遇到io阻塞时会自动切换任务

    import gevent
    
    def eat(name):
        print(f'{name} eat 1')
        gevent.sleep(2)
        print(f'{name} eat 2')
    
    def play(name):
        print(f'{name} play 1')
        gevent.sleep(3)
        print(f'{name} play 2')
    
    
    g1 = gevent.spawn(eat, 'ada')
    g2 = gevent.spawn(play, name = 'ada')
    g1.join()  #等待g1结束
    g2.join()  #等待g2结束
    print('主')
    

    情况二:from gevent import money;money.patch_all()

    from gevent import monkey;monkey.patch_all()
    import gevent
    import time
    
    def eat():
        print('eat 1')
        time.sleep(2)
        print('eat 2')
    
    def play():
        print('play 1')
        # 疯狂的计算没有io
        time.sleep(3)
        print('play 2')
    
    start = time.time()
    g1 = gevent.spawn(eat)
    g2 = gevent.spawn(play)
    g1.join()  #等待g1结束
    g2.join()  #等待g2结束
    end = time.time()
    print(end-start)
    
    
  • 相关阅读:
    利用 fsockopen() 函数开放端口扫描器
    oracle如何返回列名作为第一条数据简单解决
    redis
    MYSQL
    JUC 多线程
    java单例模式六种实现方式
    java.net.URISyntaxException 问题解决
    Calender类——字段值介绍
    Dorado开发——树形下拉框
    Java—— 一点关于String的转换
  • 原文地址:https://www.cnblogs.com/fxyadela/p/11552444.html
Copyright © 2020-2023  润新知