zoukankan      html  css  js  c++  java
  • 8.14并发编程(三)

    一、GIL全局解释器锁

      Python解释器有很多种,最常见的就是CPython解释器

      GIL本质也是一把互斥锁:将并发变成串行牺牲效率保证数据的安全

      用来阻止同一个进程下的多个线程的同时执行(同一个进程内多个线程无法实现并行但是可以实现并发)

    1.Python的多线程没法利用多核优势,是不是就是没有用了?

      GIL的存在是因为CPython解释器的内存管理不是线程安全的

      比如:垃圾回收机制

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

    第一种:如果有四个任务,是计算密集型的,需要10s

    (1)单核情况下

        开线程更省资源

    (2)多核情况下

        开进程,需要10s

        开线程,需要40s

    第二种:如果有四个任务,是IO密集型的

    (1)单核情况下

        开线程更节省资源

    (2)多核情况下

        开线程更节省资

    2.计算密集型

    # 计算密集型
    from multiprocessing import Process
    from threading import Thread
    import os,time
    def work():
        res=0
        for i in range(100000000):
            res*=i
    
    if __name__ == '__main__':
        l=[]
        print(os.cpu_count())  # 本机为6核
        start=time.time()
        for i in range(6):
            p=Process(target=work) #耗时  4.732933044433594
            # p=Thread(target=work) #耗时 22.83087730407715
            l.append(p)
            p.start()
        for p in l:
            p.join()
        stop=time.time()
        print('run time is %s' %(stop-start))

    3.IO密集型

    # IO密集型
    from multiprocessing import Process
    from threading import Thread
    import threading
    import os,time
    def work():
        time.sleep(2)
    
    if __name__ == '__main__':
        l=[]
        print(os.cpu_count()) #本机为6核
        start=time.time()
        for i in range(400):
            p=Process(target=work) #耗时9.001083612442017s多,大部分时间耗费在创建进程上
            # p=Thread(target=work) #耗时2.051966667175293s多
            l.append(p)
            p.start()
        for p in l:
            p.join()
        stop=time.time()
        print('run time is %s' %(stop-start))

      Python的多线程到底有没有用,需要看情况而定,并且肯定是有用的

      多进程 + 多线程配合使用

    二、GIL与普通的互斥锁

    from threading import Thread
    import time
    
    n = 100
    
    def task():
        global n
        tmp = n
        # time.sleep(1)  # IO操作时会自动释放GIL锁
        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)

    三、死锁

    1.Lock

    from threading import Thread,Lock
    import time
    
    mutexA = Lock()
    mutexB = Lock()
    
    class MyThread(Thread):
        def run(self):  # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发
            self.func1()
            self.func2()
    
        def func1(self):
            mutexA.acquire()  # 2号抢了A锁
            print('%s抢到了A锁'%self.name)  # self.name等价于current_thread().name
            mutexB.acquire()  # 2号此时抢不到B锁,被下面的1号抢了
            print('%s抢到了B锁'%self.name)
            mutexB.release()
            print('%s释放了B锁'%self.name)
            mutexA.release()
            print('%s释放了A锁'%self.name)
    
        def func2(self):
            mutexB.acquire()  # 1号抢了B锁
            print('%s抢到了B锁'%self.name)
            time.sleep(1)
            mutexA.acquire()  # 1号此时抢不到A锁,被上面的2号抢了
            print('%s抢到了A锁' % self.name)
            mutexA.release()
            print('%s释放了A锁' % self.name)
            mutexB.release()
            print('%s释放了B锁' % self.name)
    
    for i in range(10):
        t = MyThread()
        t.start()

      注意:自己千万不要轻易的处理锁的问题

    2.递归锁:RLock

      RLock可以被第一个抢到锁的人连续的acquire和release

      每acquire一次锁身上的计数加1

      每release一次锁身上的计数减1

      只要锁的计数不为0,其他人都不能抢

    from threading import Thread,RLock
    import time
    
    mutexA = mutexB = RLock()  # A B现在是同一把锁
    
    class MyThread(Thread):
        def run(self):  # 创建线程自动触发run方法 run方法内调用func1 func2相当于也是自动触发
            self.func1()
            self.func2()
    
        def func1(self):
            mutexA.acquire()  # A B为同一把锁,锁计数加1,为1
            print('%s抢到了A锁'%self.name)  # self.name等价于current_thread().name
            mutexB.acquire()  # 锁计数加1,为2
            print('%s抢到了B锁'%self.name)
            mutexB.release()  # 锁计数减1,为1
            print('%s释放了B锁'%self.name)
            mutexA.release()  # 锁计数减1,为0,被释放
            print('%s释放了A锁'%self.name)
    
        def func2(self):
            mutexB.acquire()  # 锁计数加1,为1
            print('%s抢到了B锁'%self.name)
            time.sleep(1)
            mutexA.acquire()  # 锁计数加1,为2
            print('%s抢到了A锁' % self.name)
            mutexA.release()  # 锁计数减1,为1
            print('%s释放了A锁' % self.name)
            mutexB.release()  # 锁计数减1,为0,被释放
            print('%s释放了B锁' % self.name)
    
    for i in range(10):
        t = MyThread()
        t.start()

    3.补充:

      只要类加括号实例化对象

      无论传入的参数是否一样生成的对象肯定不一样

      单例模式除外

    class Demo(object):
        pass
    
    obj1 = Demo()
    obj2 = Demo()
    print(id(obj1),id(obj2))  # 内存地址不一样

    四、信号量

      信号量可能在不同的领域中,对应不同的知识点

      互斥锁:可看为一个厕所(一个坑位)

      信号量:可看为公共厕所(多个坑位)

    1.怎么用:Semaphore

    from threading import Semaphore,Thread
    import time
    import random
    
    sm = Semaphore(5)  # 造了一个含有五个的坑位的公共厕所
    
    def task(name):
        sm.acquire()
        print('%s占了一个坑位'%name)
        time.sleep(random.randint(1,3))
        sm.release()
    
    for i in range(40):
        t = Thread(target=task,args=(i,))
        t.start()

    五、Event事件

      导入Event,set():发信号,wait():等待信号

      一个子进程或子线程给另一个发信号,另一个接收到信号就可以运行了

    from threading import Event,Thread
    import time
    
    e = Event()  # 先生成一个event对象
    
    def light():
        print('红灯正亮着')
        time.sleep(3)
        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()

    六、线程queue

      同一个进程下的多个线程本来就是数据共享,为什么还要用队列?

      因为队列是管道 + 锁,使用队列你就不需要自己手动操作锁的问题

      因为锁操作的不好极容易产生死锁现象

    1.Queue:先进先出

    q = queue.Queue()
    q.put('hahaha')
    q.put('hehehe')
    print(q.get())  # hahaha

    2.LifoQueue:后进先出

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

    3.PriorityQueue:优先级队列

      支持你给放入队列中的数据设置优先级

      数字越小,取出时优先级越高

    q = queue.PriorityQueue()
    q.put((10,'hahaha'))
    q.put((100,'hehehe'))
    q.put((-10,'xxxx'))  # 数字越小,优先级越高
    q.put((0,'yyyy'))
    print(q.get())  # (-10, 'xxxx')
  • 相关阅读:
    前端二维码生成方式
    svn 本地仓库使用
    layer.open实现图片预览
    基于FreethEarh框架开发的3D综合态势系统
    Cesium原理篇:6 Render模块(5: VAO&RenderState&Command)【转】
    Cesium中DrawCommand使用【转】
    Cesium案例解析(三)——Camera相机[转]
    Cesium.knockout【转】
    Java堆和栈的区别
    Kafka Eagle安装详情及问题解答
  • 原文地址:https://www.cnblogs.com/francis1/p/11353461.html
Copyright © 2011-2022 走看看