• 线程之死锁、递归锁、信号量、事件Event 、定时器


    1.死锁的现象

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

    # from threading import Thread, Lock
    # import time
    #
    # # 互斥锁的死锁
    # mutexA = Lock()
    # mutexB = Lock()
    #
    #
    # class Mythread(Thread):
    #     def run(self):
    #         self.f1()
    #         self.f2()
    #
    #     def f1(self):
    #         mutexA.acquire()
    #         print("%s get the lock A" % self.name)
    #         mutexB.acquire()
    #         print("%s get the lock B" % self.name)
    #         mutexA.release()
    #         mutexB.release()
    #
    #     def f2(self):
    #         mutexB.acquire()
    #         print("%s get the lock B" % self.name)
    #         time.sleep(0.2)
    #         mutexA.acquire()
    #         print("%s get the lock A" % self.name)
    #         mutexA.release()
    #         mutexB.release()
    #
    #
    # if __name__ == '__main__':  #死锁的现象出现是因为互斥锁只能acqurie一次不能多次,上面的实例说明:
    #     # 当线程2拿到A锁的时候去拿B锁这时候线程1还拿着B锁,导致线程2拿不到B锁,但是线程1 拿A锁的时候发现A锁在线程2
    #     # 手里拿不到,他也释放不了各自所拿的锁,就造成了死锁
    #     for i in range(10):
    #         t = Mythread()
    #         t.start()
    		
    res---->:
    	Thread-1 拿到A锁
    	Thread-1 拿到B锁
    	Thread-1 拿到B锁
    	Thread-2 拿到A锁 #出现死锁,整个程序阻塞住
    		
    		
    

    2.递归锁

    解决方法,递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
    这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁,二者的区别是:递归锁可以连续acquire多次,而互斥锁只能acquire一次.

    
    
    
    from threading import Thread, RLock
    import time
    
    # 递归锁
    mutexA = mutexB = RLock()  # 定义一个递归锁 其实是一把锁,然后可以acqurie多次 每次记录状态自加一,这样只有当状态为0 的时候才能被其他线程抢到
    
    
    class Mythread(Thread):
        def run(self):
            self.f1()
            self.f2()
    
        def f1(self):
            mutexA.acquire()
            print("%s get the lock A" % self.name)
            mutexB.acquire()
            print("%s get the lock B" % self.name)
            mutexA.release()
            mutexB.release()
    
        def f2(self):
            mutexB.acquire()
            print("%s get the lock B" % self.name)
            time.sleep(7)
            mutexA.acquire()
            print("%s get the lock A" % self.name)
            mutexA.release()
            mutexB.release()
    
    
    if __name__ == '__main__':
        for i in range(10):
            t = Mythread()
    t.start()
    
    
    

    3.信号量

    信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行,信号量同一时间可以有5个任务拿到锁去执行,如果说互斥锁是合租房屋的人去抢一个厕所,那么信号量就相当于一群路人争抢公共厕所,公共厕所有多个坑位,这意味着同一时间可以有多个人上公共厕所,但公共厕所容纳的人数是一定的,这便是信号量的大小。

    import time
    import random
    from threading import Thread, Semaphore, current_thread
    sm = Semaphore(3)  # 设置信号量 3.py   同时允许3个人蹲坑
    
    
    def task():
        """
        :param :none
        :return:
        """
        with sm: # 文件上下文管理器,内部维护了enter 方法 
            print("<%s> in toilet!" % current_thread().name)
            time.sleep(random.randint(1, 6))
            print("<%s> in over!" % current_thread().name)
    
    
    if __name__ == '__main__':
        for i in range(10):
            t = Thread(target=task)
    t.start()
    

    4.Event事件

    线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行

    # from threading import Thread, Event
    # import time
    # event = Event()  # 先生成一个Event 对象
    #
    #
    # def student(name):
    #     """
    #
    #     :param name:
    #     :return:
    #     """
    #     print("%s is having classing!" % name)
    #     event.wait()  # 学生等待老师说下课 这里就是 event wait 等待 set
    #     print("%s is having play games " % name)
    #
    #
    # def teacher(name):
    #     """
    #
    #     :param name:
    #     :return:
    #     """
    #     print("%s is having teaching!" % name)
    #     time.sleep(7)
    #     event.set() #  设置了set事件 之后 wait 的才执行下一步
    #     print("%s is having relaxing! " % name)
    #
    #
    # if __name__ == '__main__':
    #     stu1 = Thread(target=student, args=("alex",))
    #     stu2 = Thread(target=student, args=("seven",))
    #     stu3 = Thread(target=student, args=("bob",))
    #     t1 = Thread(target=teacher, args=("boss",))
    #     stu1.start()
    #     stu2.start()
    #     stu3.start()
    #     t1.start()
    
    
    	from threading import Event
    
    	event.isSet():返回event的状态值;
    
    	event.wait():如果 event.isSet()==False将阻塞线程;
    
    	event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
    
    	event.clear():恢复event的状态值为False。
    

    enter description here

    有多个工作线程尝试链接MySQL,我们想要在链接前确保MySQL服务正常才让那些工作线程去连接MySQL服务器,如果连接不成功,都会去尝试重新连接。那么我们就可以采用threading.Event机制来协调各个工作线程的连接操作

    # from threading import Thread, Event, current_thread
    # import time
    # event = Event()
    #
    # 模拟检查数据库 并且链接 
    # def conn():
    #     """
    #
    #     :return:
    #     """
    #     n = 1
    #     while not event.is_set():  # 返回标志位是否被设置 否为False
    #         if n == 4:
    #             return None
    #         event.wait(0.8)  # 0.8秒之后我就不等他设置不设置了,他已经超时了 但是我必须有三次检测的尝试所以有个n
    #         print("%s is try connect to server %s times " % (current_thread().name, n))
    #         n += 1
    #     print("%s is connecting..." % current_thread().name) # 当检测成功我就链接 set=True 我就打印这句话  set=None我就执行while
    #
    #
    #
    # def check():
    #     """
    #
    #     :return:
    #     """
    #     print("check the server is already !")
    #     time.sleep(2)
    #     event.set()
    #
    #
    # if __name__ == '__main__':
    #         for i in range(3.py):
    #             c = Thread(target=conn,)
    #             c.start()
    #
    #         ck = Thread(target=check,)
    #         ck.start()
    
    ***# 红路灯的意思模拟需要实现....***
    

    5.定时器

    定时器,指定n秒后执行某操作

    from threading import Timer
    
    def hello():
        print("hello, world")
    
    t = Timer(1, hello)
    t.start()  # after 1 seconds, "hello, world" will be printed
    

    基于定时器 开发的每隔60秒刷新验证验证码功能

    from threading import Thread, Timer
    import time
    import random
    
    
    class Code:
        def __init__(self):
            """
            实例化类得时候我先执行male_cach 方法,来生成一个code
            以后的事情就是我我的这个make_cach的方法都会有个定时器过interval时间执行本身
            生成一个新的code  用以验证
            """
            self.make_cach()
    
        def make_cach(self, interval=5):  #
    
            """
            这个方法实现了 每五秒我就会去执行一下我本身
            本身做的是:1.先运行make_code这个函数,
            生成一个code验证码全局的验证码以便make_check调用
            接着没过5秒我就会自动运行自己,来改变这个code的全局变量生成一个验证码
            :param interval:
            :return:
            """
            self.code = self.make_code()
            print(self.code)
            self.t = Timer(interval, self.make_cach)
            self.t.start()
    
        def make_code(self, n=4):  #
            """
            生成验证码的方法 放在code类里面
            :param n:create code length
            :return:
            """
            res = ""
            for i in range(n):
                s1 = str(random.randint(0, 9))
                s2 = chr(random.randint(65, 90))
                res += random.choice([s1, s2])
    
            return res
    
        def check(self):
            """
            校验验证码的 方法,注意 因为这个self.code
            一直是由于make_cack有一个定时器,导致这个值5秒就会变
            make_cach这个方法其实就是没过5秒运行本身,更新 code的值
            :return:
            """
            while 1:
                input_code = input("验证码请输入>>>:")
                if input_code.upper() == self.code:
                    self.t.cancel()
                    print("ok !")
                    break
    
    
    if __name__ == '__main__':
        obj = Code()
    obj.check()
    
  • 相关阅读:
    egret inspect插件安装失败处理方法
    SQLite – GROUP BY
    JSP Connect Database
    Sticks
    TCP-IP-part7-IP协议相关技术(一)
    Tensorflow 错误集锦
    数据结构队列的实现
    使用 Git 删除本地仓库和远端仓库文件
    垃圾回收GC
    SpringDataJpa2
  • 原文地址:https://www.cnblogs.com/zjcode/p/9024660.html
Copyright © 2020-2023  润新知