• python theading线程开发与加锁、信号量、事件等详解


    线程有2种调用方式,如下:

    直接调用

     1 import threading
     2 import time
     3  
    4 def sayhi(num): #定义每个线程要运行的函数 6 print("running on number:%s" %num) 8 time.sleep(3) 9 10 if __name__ == '__main__': 12 t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例 13 t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例 15 t1.start() #启动线程 16 t2.start() #启动另一个线程 18 print(t1.getName()) #获取线程名 19 print(t2.getName())

    继承式调用

    import threading
    import time
     
    class MyThread(threading.Thread): def __init__(self,num): threading.Thread.__init__(self) self.num = num def run(self):#定义每个线程要运行的函数 print("running on number:%s" %self.num) time.sleep(3) if __name__ == '__main__': t1 = MyThread(1) t2 = MyThread(2) t1.start() t2.start()

    同步锁(py2版本) *注:不要在3.x上运行,不知为什么,3.x上的结果总是正确的,可能是自动加了锁

     1 import time
     2 import threading
     3 
     4 
     5 def addNum():
     6     global num  # 在每个线程中都获取这个全局变量
     7     print('--get num: %s' %num)
     8     time.sleep(1)
     9     lock.acquire()  # 修改数据前加锁
    10     num -= 1  # 对此公共变量进行-1操作
    11     lock.release()  # 修改后释放
    12 
    13 num = 100  # 设定一个共享变量
    14 thread_list = []
    15 lock = threading.Lock() #生成全局锁
    16 for i in range(100):
    17     t = threading.Thread(target=addNum)
    18     t.start()
    19     print "threading num: %s 
    " %threading.active_count()  #查看线程数
    20     thread_list.append(t)
    21 
    22 for t in thread_list:  # 等待所有线程执行完毕
    23     t.join()
    24 
    25 print('final num:', num)

    死锁

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

     1 import threading
     2 import time
     3 
     4 mutexA = threading.Lock()
     5 mutexB = threading.Lock()
     6 
     7 class MyThread(threading.Thread):
     8     def __init__(self):
     9         threading.Thread.__init__(self)
    10 
    11     def run(self):
    12         self.fun1()
    13         self.fun2()
    14 
    15     def fun1(self):
    16         mutexA.acquire()
    17         print("fun1 I am %s,get res:%s---%s" % (self.name, "ResA", time.time()))
    18 
    19         mutexB.acquire()#线程2卡在这里,获取不到锁B,线程2此时已经有锁A
    20         print("fun1 I am %s,get res:%s---%s" % (self.name, "ResB", time.time())) 
    21         mutexB.release()
    22         print("FUN1 I am %s, release res:%s---%s" % (self.name, "ResB", time.time()))
    23         mutexA.release()
    24         print("FUN1 I am %s, release res:%s---%s" % (self.name, "ResA", time.time()))
    25 
    26     def fun2(self):
    27         mutexB.acquire()
    28         print("fun2 I am %s,get res:%s---%s" % (self.name, "ResB", time.time()))
    29 
    30         time.sleep(0.3) #这里整个进程sleep,
    31         print("sleep fun2 I am %s,get res:%s---%s" % (self.name, "ResB", time.time())) 
    32 
    33         mutexA.acquire()#线程1卡在这一步,线程1此时已经有锁b
    34         print("fun2 I am %s,get res:%s---%s" % (self.name, "ResA", time.time()))
    35 
    36         mutexA.release()
    37         print("FUN1 I am %s, release res:%s---%s" % (self.name, "ResA", time.time()))
    38 
    39         mutexB.release()
    40         print("FUN1 I am %s, release res:%s---%s" % (self.name, "ResB", time.time()))
    41 
    42 
    43 if __name__ == '__main__':
    44     print("start------------%s", time.time())
    45     for i in range(0, 10): #一次循环,代表一个线程
    46         print "%s 
    " %i
    47         my_thread = MyThread()
    48         my_thread.start()
    运行结果

    0

    fun1 I am Thread-1,get res:ResA---1551263063.35       #线程1获得锁B
    fun1 I am Thread-1,get res:ResB---1551263063.35       #线程1获得锁B
    FUN1 I am Thread-1, release res:ResB---1551263063.35  #线程1释放锁B
    FUN1 I am Thread-1, release res:ResA---1551263063.35  #线程1释放锁A
    fun2 I am Thread-1,get res:ResB---1551263063.35       #线程1获得锁B

    1

    fun1 I am Thread-2,get res:ResA---1551263063.35       #线程2释放锁A

    2

    ..

    9

    sleep fun2 I am Thread-1,get res:ResB---1551263065.35 #线程2释放锁B

    总结:

       1、线程1已有B锁,准备获取A锁

       2、线程2已有A锁,准备获取B锁

       1和2同一时刻,因此互相等待

    RLock(递归锁)

    说白了就是在一个大锁中还要再包含子锁

    在python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。

     1 Rlock = threading.RLock()
     2 class MyThread(threading.Thread):
     3     def __init__(self):
     4         threading.Thread.__init__(self)
     5 
     6     def run(self):
     7         self.fun1()
     8         self.fun2()
     9 
    10     def fun1(self):
    11         Rlock.acquire()  # 如果锁被占用,则阻塞在这里,等待锁的释放
    12         print("I am %s,get res:%s---%s" % (self.name, "ResA", time.time()))
    13         Rlock.acquire()  # count=2
    14         print("I am %s,get res:%s---%s" % (self.name, "ResB", time.time()))
    15 
    16         Rlock.release()  # count-1
    17         Rlock.release()  # count-1=0
    18 
    19     def fun2(self):
    20         Rlock.acquire()  # count=1
    21         print("I am %s,get res:%s---%s" % (self.name, "ResB", time.time()))
    22         time.sleep(0.2)
    23 
    24         Rlock.acquire()  # count=2
    25         print("I am %s,get res:%s---%s" % (self.name, "ResA", time.time()))
    26         Rlock.release()  # coun-1
    27         Rlock.release()  # count-1
    28 
    29 
    30 if __name__ == '__main__':
    31     print("start-----------%s" % time.time())
    32     for i in range(0, 10):
    33         my_thread = MyThread()
    34         my_thread.start()

    Semaphore(信号量)

    原理:来限制一个时间点内的线程数量。比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。

     1 def run(n):
     2     semaphore.acquire()
     3     time.sleep(1)
     4     print("run the thread: %s
    " %n)
     5     semaphore.release()
     6  
     7 if __name__ == '__main__':
     8     num= 0
     9     semaphore  = threading.BoundedSemaphore(5) #最多允许5个线程同时运行
    10     for i in range(20):
    11         t = threading.Thread(target=run,args=(i,))
    12         t.start()
    13  
    14 while threading.active_count() != 1:
    15     pass #print threading.active_count()
    16 else:
    17     print('----all threads done---')
    18     print(num)

    Events

    通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即起动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则。

    红绿灯

     1 import threading,time
     2 import random
     3 def light():
     4     if not event.isSet():
     5         event.set() #wait就不阻塞 #绿灯状态
     6     count = 0
     7     while True:
     8         if count < 10:
     9             print('33[42;1m--green light on---33[0m')
    10         elif count <13:
    11             print('33[43;1m--yellow light on---33[0m')
    12         elif count <20:
    13             if event.isSet():
    14                 event.clear()
    15             print('33[41;1m--red light on---33[0m')
    16         else:
    17             count = 0
    18             event.set() #打开绿灯
    19         time.sleep(1)
    20         count +=1
    21 def car(n):
    22     while 1:
    23         time.sleep(random.randrange(10))
    24         if  event.isSet(): #绿灯
    25             print("car [%s] is running.." % n)
    26         else:
    27             print("car [%s] is waiting for the red light.." %n)
    28 if __name__ == '__main__':
    29     event = threading.Event()
    30     Light = threading.Thread(target=light)
    31     Light.start()
    32     for i in range(3):
    33         t = threading.Thread(target=car,args=(i,))
    34         t.start()

    员工过门禁

     1 import threading
     2 import time
     3 import random
     4 
     5 def door():
     6     door_open_time_counter = 0
     7     while True:
     8         if door_swiping_event.is_set():
     9             print("33[32;1mdoor opening....33[0m")
    10             door_open_time_counter +=1
    11         else:
    12             print("33[31;1mdoor closed....,please swipe to open.33[0m")
    13             door_open_time_counter = 0 #清空计时器
    14             door_swiping_event.wait()
    15         if door_open_time_counter > 3:#门开了已经3s了,该关了
    16             door_swiping_event.clear()
    17         time.sleep(0.5)
    18 
    19 def staff(n):
    20     print("staff [%s] is comming..." % n )
    21     while True:
    22         if door_swiping_event.is_set():
    23             print("33[34;1mdoor is opened, staff [%s] passing.....over!!33[0m" % n )
    24             break
    25         else:
    26             print("STAFF [%s] sees door got closed, swipping the card....." % n)
    27             door_swiping_event.set()
    28             print("after set,,,  staff [%s]--------" %n,door_swiping_event.is_set())
    29         # time.sleep(0.5)
    30 
    31 door_swiping_event  = threading.Event() #设置事件
    32 door_thread = threading.Thread(target=door)
    33 door_thread.start()
    34 for i in range(5):
    35     p = threading.Thread(target=staff,args=(i,))
    36     time.sleep(random.randrange(3))
    37     p.start()
    运行结果
    door closed....,please swipe to open.
    staff [0] is comming...
    STAFF [0] sees door got closed, swipping the card.....
    ('after set,,,  staff [0]--------', True)
    door is opened, staff [0] passing.....over!!
    door opening....
    door opening....
    door opening....
    door opening....
    staff [1] is comming...
    STAFF [1] sees door got closed, swipping the card.....
    ('after set,,,  staff [1]--------'staff [2] is comming..., 
    Truedoor is opened, staff [2] passing.....over!!)
    
    door is opened, staff [1] passing.....over!!
    door opening....
    door closed....,please swipe to open.
    staff [3] is comming...
    STAFF [3] sees door got closed, swipping the card.....
    ('after set,,,  staff [3]--------', True)
    door is opened, staff [3] passing.....over!!
    door opening....
    door opening....
    staff [4] is comming...
    door is opened, staff [4] passing.....over!!
    door opening....
    door opening....
    door closed....,please swipe to open.
  • 相关阅读:
    jenkins结合cygwin软件实现从centos发布代码rsync到windows server2019的过程
    ansible的playbook创建nginx站点配置示例
    windows环境下搭建redis/php7.2/nginx1.18环境
    aws亚马逊购买负载均衡alb和ec2服务器备注
    Window server 2019安装.net3.5并通过jenkins发布c#代码
    windows server2019环境下安装openssh进行jenkins代码golang发布
    进程管理脚本包括start/stop/restart/status
    通过dockerfile制作基于centos8系统的nginx镜像
    创建基于harbor的docker仓库
    jenkins配置添加windows节点做ui自动化测试
  • 原文地址:https://www.cnblogs.com/kevincaptain/p/10445048.html
Copyright © 2020-2023  润新知