zoukankan      html  css  js  c++  java
  • day32 GIL全局解释器锁 死锁 信号量 Event事件 线程

    练习:

    想与多个用户进行通讯,且支持并发

    实现从单线程,无法并发变成多线程,支持并发

    import socket
    from threading import Thread
    
    
    server = socket.socket()
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    def talk(conn):
        while True:
            try:
                data = conn.recv(1024)
                if not len(data):
                    break
                conn.send(data.upper())
            except ConnectionResetError as a:
                print(a)
                break
        conn.close()
    
    
    while True:
        conn,addr = server.accept()
        # talk(conn)
        t = Thread(target=talk,args=(conn,))
        t.start()
    练习

    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.引用计数

    引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。

    2.标记清除

    圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间.

    3.分代回收

    1.刚产生的变量值放在新生代中高频率检查,如果引用计数为0,就是采用引用计数机制回收,长期存活的变量值经过多次检查后会提高分代
    2.分带越高,检查频率越低,且还能继续提高一直存活的变量值的分带,从而来提高整体垃圾回收的效率
    

    GIL的加锁与解锁时机

    加锁的时机:在调用解释器时立即加锁

    解锁时机:

    • 当前线程遇到了IO时释放

    • 当前线程执行时间超过设定值时释放

    GIL的优点:

    • 保证了CPython中的内存管理是线程安全的

    GIL的缺点:

    • 互斥锁的特性使得多线程无法并行

    ### 问题
    
    GIL是python的特点吗?
    
    不是,它是CPython解释器的特点,仅在CPython中存在.
    
    单进程下多个线程无法利用多核优势,是所有解释性语言的通病.
    
    针对不同的数据应该加不同的锁进行处理
    

      

    研究python的多线程是否有用需要分情况讨论

    四个任务 计算密集型的  10s
    单核情况下
        开线程更省资源
    多核情况下
        开进程 10s
        开线程 40s
        
    四个任务 IO密集型的  
    单核情况下
        开线程更节省资源
    多核情况下
        开线程更节省资源 
    

      

    from multiprocessing import Process
    from threading import Thread
    import os , time
    
    
    # 计算密集型
    def work():
        res = 0
        for i in range(1000):
            res*=i
    
    if __name__ == '__main__':
         l = []
         print(os.cpu_count())
         start = time.time()
         for i in range(6):
            # p = Process(target=work)
            #创建进程
            p = Thread(target=work)
            # 创建线程
            l.append(p)
            p.start()
         for p in l:
             p.join()
         stop = time.time()
         print('run time is %s'%(stop-start))
    计算密集型
    from multiprocessing import Process
    from threading import Thread
    import os , time
    
    # io密集型
    def work():
        time.sleep(1)
    
    if __name__ == '__main__':
        l = []
        print(os.cpu_count())
        start = time.time()
        for i in range(400):
            p = Process(target=work)
            l.append(p)
            p.start()
        for p in l:
            # print(p)
            p.join()
        stop = time.time()
        print('run time is %s'%(stop-start))
    io密集型

    死锁

    死锁问题 当程序出现了不止一把锁,分别被不同的线程持有, 有一个资源 要想使用必须同时具备两把锁 这时候程序就会进程无限卡死状态 ,这就称之为死锁

    from threading import Thread,Lock
    
    
    mutex1 = Lock()
    mutex2 = Lock()
    
    
    class MyThread(Thread):
        def run(self):
            self.func1()
            self.func2()
    
        def func1(self):
            mutex1.acquire()
            print('%s 抢到了锁1'%self.name)
            mutex2.acquire()
            print('%s 抢到了锁2' % self.name)
            mutex1.release()
            print('%s 释放了锁1'%self.name)
            mutex2.release()
            print('%s 释放了锁2' % self.name)
    
        def func2(self):
            mutex2.acquire()
            print('%s 抢到了锁2'%self.name)
            mutex1.acquire()
            print('%s 抢到了锁1' % self.name)
            mutex2.release()
            print('%s 释放了锁2'%self.name)
            mutex1.release()
            print('%s 释放了锁1' % self.name)
    
    for i in range(10):
        t = MyThread()
        t.start()
    死锁

    可重入锁

    Rlock 称之为递归锁或者可重入锁

    Rlock不是用来解决死锁问题的

    与Lock唯一的区别: Rlock同一线程可以多次执行acquire 但是执行几次acquire就应该对应release几次 如果一个线程已经执行过acquire 其他线程将无法执行acquire

    from threading import Thread,RLock
    """
    Rlock可以被第一个抢到锁的人连续的acquire和release
    每acquire一次锁身上的计数加1
    每release一次锁身上的计数减1
    只要锁的计数不为0 其他人都不能抢
    
    """
    
    mutex1 = mutex2 = RLock()
    
    class MyThread(Thread):
        def run(self): # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发
            self.func1()
            self.func2()
    
        def func1(self):
            mutex1.acquire()
            print('%s 抢到了锁1'%self.name)# self.name等价于current_thread().name
            mutex2.acquire()
            print('%s 抢到了锁2' % self.name)
            mutex1.release()
            print('%s 释放了锁1'%self.name)
            mutex2.release()
            print('%s 释放了锁2' % self.name)
    
        def func2(self):
            mutex2.acquire()
            print('%s 抢到了锁2'%self.name)
            mutex1.acquire()
            print('%s 抢到了锁1' % self.name)
            mutex2.release()
            print('%s 释放了锁2'%self.name)
            mutex1.release()
            print('%s 释放了锁1' % self.name)
    
    for i in range(10):
        t = MyThread()
        t.start()
    '''
    只要类加括号实例化对象
    无论传入的参数是否一样生成的对象肯定不一样
    单例模式除外
    '''
    可重入锁

    信号量

    信号量可能在不同的领域中 对应不同的知识点
    可以现在被锁定的代码 同时可以被多少线程并发访问
    Lock 锁住一个马桶  同时只能有一个
    Semaphore 锁住一个公共厕所    同时可以来一堆人
    
    
    用途: 仅用于控制并发访问   并不能防止并发修改造成的问题
    
    from threading import Thread,Semaphore
    import time
    import random
    
    a = Semaphore(5)
    # 造了一个含有五个的坑位的公共厕所
    
    def task(name):
        a.acquire()
        print('%s 在上厕所'%name)
        time.sleep(random.randint(1,3))
        a.release()
    
    
    for i in range(10):
        t = Thread(target=task,args=(i,))
        t.start()
    信号量

    Event事件

    Event事件
    事件表示在某个时间发生了某个事情的通知信号,用于线程间协同工作。
    
    因为不同线程之间是独立运行的状态不可预测,
    所以一个线程与另一个线程间的数据是不同步的
    当一个线程需要利用另一个线程的状态来确定自己的下一步操作时,
    就必须保持线程间数据的同步,Event就可以实现线程间同步
    
    from threading import Thread,Event
    import time
    
    # 生成一个event对象
    e = Event()
    def light():
        print('红灯亮')
        time.sleep(1)
        e.set()  # 发送信号
        print('绿灯亮')
    
    
    def car(name):
        print('%s 正在等红灯'% name)
        e.wait()  #等待信号
        print('%s 加油门,踩离合'% name)
    
    
    t = Thread(target=light)
    t.start()
    
    for i in range(10):
        t = Thread(target=car,args=('萝卜%s'%i,))
        t.start()
    event事件
    event.isSet():返回event的状态值;
    event.wait():将阻塞线程;知道event的状态为True
    event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
    event.clear():恢复event的状态值为False。
    

      

    线程队列

    同一个进程下的多个线程本来就是数据共享 为什么还要用队列???
    
    因为队列是管道+锁  使用队列你就不需要自己手动操作锁的问题 
    
    因为锁操作的不好极容易产生死锁现象
    

     

    1.Queue 先进先出队列

    与多进程中的Queue使用方式完全相同,区别仅仅是不能被多进程共享。

    import queue
    q = queue.Queue()
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get())
    print(q.get())
    1

    2.LifoQueue 后进先出队列

    该队列可以模拟堆栈,实现先进后出,后进先出

    import queue
    q = queue.LifoQueue()
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get())
    print(q.get())
    2

    3.PriorityQueue 优先级队列

    该队列可以为每个元素指定一个优先级,这个优先级可以是数字,字符串或其他类型,但是必须是可以比较大小的类型,取出数据时会按照从小到大的顺序取出

    import queue
    q = queue.PriorityQueue()
    
    q.put((10,'hehe'))
    q.put((20,'enen'))
    q.put((-10,'wenwen'))
    # 取值,得到的是个元组,可以根据索引取值
    print(q.get()[1])
    print(q.get())
    print(q.get())
    print(q.get())
    3
  • 相关阅读:
    HDU3107 Godfather(树的重心)
    POJ1655 Balancing Act(树的重心)
    codeforces 691F Couple Cover(暴力预处理)
    codeforces 691E Xor-sequences(矩阵快速幂)
    codeforces 691D Swaps in Permutation(并查集)
    HDU5727 Necklace(环排+匈牙利)
    codeforces679C Bear and Square Grid(dfs优化)
    codeforces679B Bear and Tower of Cubes(思路)
    spring boot
    spring boot资料收集
  • 原文地址:https://www.cnblogs.com/komorebi/p/11354397.html
Copyright © 2011-2022 走看看