• TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q


    TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q

    一、TCP协议下的服务端并发

    '''
    将不同的功能尽量拆分成不同的函数,拆分出来的功能可以被多个地方使用
    TCP服务端实现并发
    	1、将连接循环和通信循环拆分成不同的函数
    	2、将通信循环做成多线程
    '''
    
    # 服务端
    import socket
    from threading import Thread
    
    '''
    服务端
        要有固定的IP和PORT
        24小时不间断提供服务
        能够支持并发
    '''
    server = socket.socket()
    server.bind(('127.0.0.1', 8080))
    server.listen(5)
    
    
    def talk(conn):
        while True:
            try:
                data = conn.recv(1024)
                if len(data) == 0: break
                print(data.decode('utf-8'))
                conn.send(data.upper())
            except ConnectionResetError as e:
                print(e)
                break
        conn.close()
    
    
    while True:
        conn, addr = server.accept()
        print(addr)
        t = Thread(target=talk, args=(conn,))
        t.start()
        
        
    # 客户端
    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1', 8080))
    
    while True:
        client.send(b'hello')
        data = client.recv(1024)
        print(data.decode('utf-8'))
    

    二、GIL全局解释器锁与python多线程

    '''
    在CPython中才有GIL的概念,不是python的特点
    GIL也是一把互斥锁
    	将并发变成串行,牺牲了效率但是提高了数据的安全
    	ps:
    		1、针对不同的数据,应该使用不同的锁去处理
    		2、自己不要轻易的处理锁的问题,哪怕你知道acquire和release
    		当业务逻辑稍微复杂的一点情况下,极容易造成死锁
    CPython中的GIL的存在是因为python的内存管理不是线程安全
    GIL类似于是加在解释器上面的一把锁
    '''
    
    '''
    内存管理
    	引用计数:值与变量的绑定关系的个数
    	标记清除:当内存快要满的时候,会自动停止程序的运行,检测所有的变量与值的绑定关系
    			给没有绑定关系的值打个标记,最后一次性清除
    	分代回收:垃圾回收机制也是需要消耗资源的,而正常一个程序的运行内部会使用很多变量与值,
    			并且有一部分类似于常量,减少垃圾回收消耗的时间,应该对变量与值的绑定关系做一个分类
    			新生代》》》青春代》》》老年代
    			垃圾回收机制扫描一定次数发现关系还在,会将该关系移至下一代
    			随着代数的递增,扫描频率是降低的
    '''
    
    '''
    In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple
    native threads from executing Python bytecodes at once. This lock is necessary mainly
    because CPython’s memory management is not thread-safe.
    '''
    '''
    ps:python解释器有很多种  最常见的就是Cpython解释器
    GIL本质也是一把互斥锁:将并发变成串行牺牲效率保证数据的安全 
    用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行但是可以实现并发)
        python的多线程没法利用多核优势  是不是就是没有用了?
        
    GIL的存在是因为CPython解释器的内存管理不是线程安全的
    
    垃圾回收机制
        1.引用计数
        2.标记清除
        3.分代回收
    
    
    研究python的多线程是否有用需要分情况讨论
    四个任务 计算密集型的  10s
    单核情况下
        开线程更省资源
    多核情况下
        开进程 10s
        开线程 40s
    
    四个任务 IO密集型的  
    单核情况下
        开线程更节省资源
    多核情况下
        开线程更节省资源
    '''
    # 计算密集型
    from multiprocessing import Process
    from threading import Thread
    import os
    import time
    
    
    def work():
        res = 0
        for i in range(100000000):
            res *= i
    
    
    if __name__ == '__main__':
        l = []
        print(os.cpu_count())
        start = time.time()
        for i in range(6):
            p = Process(target=work)  # 耗时10.415400505065918
            # p = Thread(target=work)  # 耗时35.51156568527222
            l.append(p)
            p.start()
        for p in l:
            p.join()
        stop = time.time()
        print(f'running time is {stop - start}')
        
    # IO密集型
    from multiprocessing import Process
    from threading import Thread
    import time
    
    
    def work():
        time.sleep(2)
    
    
    if __name__ == '__main__':
        l = []
        start = time.time()
        for i in range(400):
            p = Process(target=work)  # 耗时10.607022523880005
            # p = Thread(target=work)  # 耗时2.046351194381714
            l.append(p)
            p.start()
        for p in l:
            p.join()
        stop = time.time()
        print(f'running time is {stop - start}')
    '''
    python的多线程到底有没有用
    需要看情况而定  并且肯定是有用的
    
    以后的用法就是多线程和多进程配合使用
    '''
    

    三、GIL和普通的互斥锁

    from threading import Thread
    import time
    
    n = 100
    
    
    def task():
        global n
        tmp = n
        time.sleep(1)
        n = tmp - 1
    
    
    t_list = []
    for i in range(100):
        t = Thread(target=task)
        t.start()
        t_list.append(t)
    for t in t_list:
        t.join()
    print(n)
    

    四、死锁和递归锁

    # 死锁现象
    from threading import Thread,Lock
    import time
    
    mutexA = Lock()
    mutexB = Lock()
    
    class MyThread(Thread):
        def run(self):
            self.func1()
            self.func2()
    
        def func1(self):
            mutexA.acquire()
            print(f'{self.name}抢到了A锁')
            mutexB.acquire()
            print(f'{self.name}抢到了B锁')
            mutexB.release()
            print(f'{self.name}释放了B锁')
            mutexA.release()
            print(f'{self.name}释放了A锁')
    
        def func2(self):
            mutexB.acquire()
            print(f'{self.name}抢到了B锁')
            time.sleep(1)
            mutexA.acquire()
            print(f'{self.name}抢到了A锁')
            mutexA.release()
            print(f'{self.name}释放了A锁')
            mutexB.release()
            print(f'{self.name}释放了B锁')
    
    
    for i in range(10):
        t = MyThread()
        t.start()
    '''
    程序运行几步后,就出现了卡住的现象,同时有两个线程要抢对方手里的锁
    
    Thread-1抢到了A锁
    Thread-1抢到了B锁
    Thread-1释放了B锁
    Thread-1释放了A锁
    Thread-1抢到了B锁
    Thread-2抢到了A锁
    '''
    
    # 递归锁解决死锁问题
    '''
    递归锁:可以被第一个抢到该锁的人多次的acquire和release,内部有一个计数
    		acquire加一
    		release减一
    		当别人在抢这把锁的时候,只要计数不为零,永远也别想抢到
    '''
    from threading import Thread, RLock
    import time
    
    mutexA = mutexB = RLock()
    
    
    class MyThread(Thread):
        def run(self):
            self.func1()
            self.func2()
    
        def func1(self):
            mutexA.acquire()
            print(f'{self.name}抢到了A锁')
            mutexB.acquire()
            print(f'{self.name}抢到了B锁')
            mutexB.release()
            print(f'{self.name}释放了B锁')
            mutexA.release()
            print(f'{self.name}释放了A锁')
    
        def func2(self):
            mutexB.acquire()
            print(f'{self.name}抢到了B锁')
            time.sleep(1)
            mutexA.acquire()
            print(f'{self.name}抢到了A锁')
            mutexA.release()
            print(f'{self.name}释放了A锁')
            mutexB.release()
            print(f'{self.name}释放了B锁')
    
    
    for i in range(10):
        t = MyThread()
        t.start()
    '''
    程序正常运行结束,死锁问题得到解决
    '''
    

    五、信号量

    # 信号量在不同的领域,对应不同的含义
    '''
    互斥锁:一个厕所(一个坑位)
    信号量:公共厕所(多个坑位)
    '''
    from threading import Thread, Semaphore
    import time
    import random
    
    sm = Semaphore(5)  # 造了一个含有五个坑位的公共厕所
    
    
    def task(name):
        sm.acquire()
        print(f'{name}占了一个坑位')
        time.sleep(random.randint(1, 3))
        sm.release()
    
    
    for i in range(40):
        t = Thread(target=task, args=(i,))
        t.start()
        
    '''
    同时只能有五个线程操作被锁住的内容
    '''
    

    六、event事件

    from threading import Event, Thread
    import time
    
    e = Event()
    
    
    def light():
        print('红灯正亮着')
        time.sleep(3)
        e.set()  # 等信号
        print('绿灯亮了')
    
    
    def car(name):
        print(f'{name}正在等红灯')
        e.wait()  # 发信号
        print(f'{name}加油门飙车了')
    
    
    t = Thread(target=light)
    t.start()
    
    for i in range(10):
        t = Thread(target=car, args=(f'伞兵{i}号',))
        t.start()
    '''
    所有的线程都在等信号
    
    红灯正亮着
    伞兵0号正在等红灯
    伞兵1号正在等红灯
    伞兵2号正在等红灯
    伞兵3号正在等红灯
    伞兵4号正在等红灯
    伞兵5号正在等红灯
    伞兵6号正在等红灯
    伞兵7号正在等红灯
    伞兵8号正在等红灯
    伞兵9号正在等红灯
    绿灯亮了
    伞兵7号加油门飙车了
    伞兵6号加油门飙车了
    伞兵9号加油门飙车了
    伞兵4号加油门飙车了
    伞兵1号加油门飙车了
    伞兵3号加油门飙车了
    伞兵8号加油门飙车了
    伞兵5号加油门飙车了
    伞兵0号加油门飙车了
    伞兵2号加油门飙车了
    '''
    

    七、线程q

    '''
    同一个进程下的多个线程本来就是数据共享的,为什么还要用队列呢?
    因为队列是管道+锁,使用队列你就不需要自己手动操作锁的问题,避免了死锁现象
    '''
    import queue
    
    # 普通的队列
    q1 = queue.Queue()
    q1.put('hahah')
    print(q1.get())
    
    # 先进后出的队列
    q2 = queue.LifoQueue()
    q2.put(1)
    q2.put(2)
    q2.put(3)
    print(q2.get())
    
    # 优先级队列
    q3 = queue.PriorityQueue()
    # 数字越小,优先级越高
    q3.put((10, 'haha'))
    q3.put((0, 'xixi'))
    q3.put((100, 'hehe'))
    print(q3.get())
    
  • 相关阅读:
    数据库设计主键定义思考
    dotnet(C#)的面试题,大家共享一下
    一些有创意的SQL语句
    asp.net(c#) 服务器探针
    存储过程共有三种返回值
    如何删除表中的重复记录?等等常用SQL语句的积累
    一般存储过程示例
    关于utf8,unicode字符集
    在Asp.net里利用DIV层元素弹出窗体
    数据库主键设计思考
  • 原文地址:https://www.cnblogs.com/DcentMan/p/11380546.html
Copyright © 2020-2023  润新知