• 线程(中)


    一、线程锁

    from threading import Thread,Lock
    
    l = Lock()  #实例化一个锁对象
    x = 0
    def task():
        global x
        l.acquire()  # 拿钥匙
        for i in range(100000):  #252990  出现了运算错误  由于三个线程抢占cpu导致的,所以我们需要给他加锁
            x +=1
        l.release()  #还钥匙   
    
    if __name__ == '__main__':
        t1 = Thread(target=task)
        t2 = Thread(target=task)
        t3 = Thread(target=task)
    
    l_t = [t1,t2,t3]
    for t in l_t:
        t.start()
    t.join()
    print(x)
    
    '''
    300000   这样结果就再也没有变过
    '''

    二、死锁和递归锁


    死锁:

    ​ 两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程

    举个栗子:三个人吃面

    from threading import Thread,Lock
    import time
    
    chopsticks_lock = Lock()  #实例化一个筷子锁
    noodles_lock = Lock()    #实例化一个面条锁
    def eat1(name):  #开启第一个线程
        chopsticks_lock.acquire()  #给筷子加锁
        print(f"{name }--抢到了筷子")
        noodles_lock.acquire()   #给面条加锁
        print(f"{name }--抢到了面条")
        print(f"{name }--在吃面")
        chopsticks_lock.release()  #还钥匙
        noodles_lock.release()    #还钥匙
    
    
    def eat2(name):  #开启第二个线程
        noodles_lock.acquire()  # 给面条加锁
        print(f"{name}--抢到了面条")
        time.sleep(1)
        chopsticks_lock.acquire()  # 给筷子加锁
        print(f"{name }--抢到了筷子")
        print(f"{name }--在吃面")
        chopsticks_lock.release()  # 还钥匙
        noodles_lock.release()  # 还钥匙
    
    
    if __name__ == '__main__':
        l_name = ['线程1','线程2','线程3']   #定义一个吃面的人的列表
        for name in l_name:
            t1 = Thread(target=eat1,args=(name,))   #实例化第一个线程
            t2 = Thread(target=eat2,args=(name,))   #实例化第二个线程
            t1.start()
            t2.start()
    
    
    '''
    线程1--抢到了筷子
    线程1--抢到了面条
    线程1--在吃面
    线程1--抢到了面条
    线程2--抢到了筷子
    '''
    #这样就造成了死锁问题,杨蓬蓬--抢到了面条,王文彬--抢到了筷子,导致两个人都吃不了面,只有同时拿到面条课筷子才能吃面
    '''
    线程2拿到了(锁头1-筷子)想要往下执行需要(锁头2-面条)
    线程1拿到了(锁头2-面条)想要往下执行需要(锁头1-筷子),
    互相都拿到了彼此想要往下执行的必需条件,互相都不放手里的锁头.
    '''

    用递归锁解决死锁问题:

    递归锁:

    ​ 在同一个线程内可以被多次acquire

    ​ 就上面三个人吃面的例子,我们所说的死锁也就是带有筷子锁和面条所得一串锁,只要一串锁拿走了,其他人就拿不到锁。

    from threading import Thread,Lock,RLock
    import time
    chopsticks_lock = noodles_lock = RLock()  #实例化一个递归锁
    def eat1(name):  #开启第一个线程
        chopsticks_lock.acquire()  #给筷子加锁
        print(f"{name }--抢到了筷子")
        time.sleep(1)
        noodles_lock.acquire()   #给面条加锁
        print(f"{name }--抢到了面条")
        print(f"{name }--在吃面")
        chopsticks_lock.release()  #还钥匙
        noodles_lock.release()    #还钥匙
    
    
    def eat2(name):  #开启第二个线程
        noodles_lock.acquire()  # 给面条加锁
        print(f"{name}--抢到了面条")
        time.sleep(1)
        chopsticks_lock.acquire()  # 给筷子加锁
        print(f"{name }--抢到了筷子")
        print(f"{name }--在吃面")
        chopsticks_lock.release()  # 还钥匙
        noodles_lock.release()  # 还钥匙
    
    
    if __name__ == '__main__':
        l_name = ['线程1','线程2','线程3']   #定义一个吃面的人的列表
        for name in l_name:
            t1 = Thread(target=eat1,args=(name,))   #实例化第一个线程
            t2 = Thread(target=eat2,args=(name,))   #实例化第二个线程
            t1.start()
            t2.start()
    '''
    线程1--抢到了筷子
    线程1--抢到了面条
    线程1--在吃面
    线程1--抢到了面条
    线程1--抢到了筷子
    线程1--在吃面
    线程2--抢到了筷子
    线程2--抢到了面条
    线程2--在吃面
    线程2--抢到了面条
    线程2--抢到了筷子
    线程2--在吃面
    线程3--抢到了筷子
    线程3--抢到了面条
    线程3--在吃面
    线程3--抢到了面条
    线程3--抢到了筷子
    线程3--在吃面
    '''

    1|3三、信号量(Semaphore)


    规定几个线程一同运行,就必须几个线程一同运行,不能多,例如KTV只能一次进4个人,只有有人走了,其他人才能进去。那我们就来举一个KTV的例子

    from threading import Thread,Semaphore
    import time
    
    def KTV(name):
        sm.acquire()
        print(f"{name}走进了KTV")
        time.sleep(2)
        print(f"{name}走出了KTV")
        time.sleep(4)
        sm.release()
    
    if __name__ == '__main__':
        sm = Semaphore(4)  #规定一次只能有4个人进入KTV
        for i in range(15):
            t = Thread(target=KTV,args=(i+1,))
            t.start()
            
            
    '''
    1走进了KTV
    2走进了KTV
    3走进了KTV
    4走进了KTV
    
    1走出了KTV
    4走出了KTV
    2走出了KTV
    3走出了KTV
    ......
    5走进了KTV
    7走进了KTV
    6走进了KTV
    8走进了KTV
    '''

    四、GIL


    在Cpython解释器中有一把GIL锁(全局解释器锁),GIl锁本质是一把互斥锁。导致了同一个进程下,同一时间只能运行一个线程,无法利用多核优势.同一个进程下多个线程只能实现并发不能实现并行.

    为什么要有GIL?
    因为cpython自带的垃圾回收机制不是线程安全的,所以要有GIL锁.

    计算密集型 :推荐使用多进程
    每个都要计算10s
    多线程
    在同一时刻只有一个线程会被执行,也就意味着每个10s都不能省,分开每个都要计算10s,共40.ns
    多进程
    可以并行的执行多个线程,10s+开启进程的时间

    IO密集型: 推荐使用多线程
    4个任务每个任务90%大部分时间都在io.
    多线程
    可以实现并发,每个线程io的时间不咋占用cpu, 10s + 4个任务的计算时间
    多进程
    可以实现并行,10s+1个任务执行的时间+开进程的时间

    下面我们就来看这样一个例子:

    '''计算密集型'''
    #推荐使用多进程
    from threading import Thread
    from multiprocessing import Process
    import time
    
    def task():
        x = 0
        for i in range(10000000):
            x *= i
    
    
    if __name__ == '__main__':
        list = []
        start = time.time()
        for i in range(4):
            # p = Process(target=task) #实例化一个子进程
            # list.append(p)
            # p.start()
            t = Thread(target=task)
            list.append(t)
            t.start()
        for m in list:
            m.join()
        end= time.time()
        # print('多进程',end-start) #多进程 2.8643341064453125
        print('多线程', end-start)  #多线程 3.020913600921631
    '''看这个时间你就会一目了然'''
    from threading import Thread
    from multiprocessing import Process
    import time
    
    def task():
        time.sleep(2)
        print('哈哈哈!好好学习天天向上!!!')
    
    
    if __name__ == '__main__':
        list = []
        start = time.time()
        for i in range(4):
            p = Process(target=task) #实例化一个子进程
            list.append(p)
            p.start()
            # t = Thread(target=task)
            # list.append(t)
            # t.start()
        for m in list:
            m.join()
        end= time.time()
        print('多进程',end-start) #多进程 3.662198781967163
        # print('多线程', end-start)  #多线程 2.0046467781066895
    '''看这个时间你就会一目了然''' 
  • 相关阅读:
    201521123036 《Java程序设计》第1周学习总结
    201521123075 《Java程序设计》第2周学习总结
    201521123075 《Java程序设计》第1周学习总结
    201521123039 《java程序设计》第一周学习总结(新)
    201521123039 《java程序设计》第一周学习总结
    201521123084 《Java程序设计》第2周学习总结
    Problem
    第一周作业及第一周学习总结
    Hello
    201521123092《java程序设计》第八周学习总结
  • 原文地址:https://www.cnblogs.com/lulingjie/p/11545531.html
Copyright © 2020-2023  润新知