• Python之多线程开发(一)


    使用Lock和RLock对象

      如果多个线程共同对某个数据进行修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步修改,在Python程序中,使用对象Lock和RLock

    可以实现简单的线程同步功能,这两个对象都有acquire方法和release方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到acquire和release方法之间。

    多线程的优势在于可以同时运行多个任务(感觉上是这样),但是当线程需要共享数据时,可能存在数据不同步的问题。以下代码演示了使用RLock实现线程同步。

    import threading
    import time
    class mt(threading.Thread):
        def run(self):
            global x
    
            lock.acquire()
            for i in range(5):
                x += 10
            time.sleep(1)
            print(x)
            lock.release()
    
    x = 0
    lock = threading.RLock()
    
    def main():
        thrs = []
        for item in range(8):
            thrs.append(mt())
    
        for item in thrs:
            item.start()
    if __name__ == '__main__':
        main()

    上述实例中,自定义了一个带锁访问全局变量x的线程类mt,在主函数main()中初始化了8个线程来修改x,在同一时间只能由一个线程对x进行操作,执行效果如下:

    50,100,150....。在python程序中,要想让可变对象安全的用在多线程环境下,可以利用threading中的Lock对象来解决,以下代码演示了Lock对临界区加锁的过程。

    import threading
    
    class ShareCounter:
    
        def __init__(self, initial_value=0):
            self._value = initial_value
            self._value_lock = threading.Lock()
    
        def incr(self, delta=1):
            with self._value_lock:
                self._value += delta
    
        def decr(self, delta=1):
            with self._value_lock:
                self._value -= delta
    
    def test(c):
        for n in range(10000):
            c.incr()
    
        for n in range(10000):
            c.decr()
    
    if __name__ == '__main__':
        c = ShareCounter()
        t1 = threading.Thread(target=test, args=(c,))
        t2 = threading.Thread(target=test, args=(c,))
        t3 = threading.Thread(target=test, args=(c,))
    
        t1.start()
        t2.start()
        t3.start()
        print('Runing test')
        t1.join()
        t2.join()
        t3.join()
        
        assert  c._value == 0
        print('Locks good!',c._value) # 0

    上述代码中,当使用with语句时,Lock对象可确保产生互斥的行为,也就是说在同一时间只运行一个线程执行with里面的语句。

    使用Condition对象

    在Python中,使用Condition对象可以在某些事件触发或达到特定条件下后才能处理数据,提供的Condition对象目的就是实现对复杂线程同步问题的支持,

    Condition通常和一个锁关联,当需要在多个Condition中共享一个锁时,可以传递一个Lock/RLock实例,否则它会自动生成一把锁。下面代码会使用

    Conditon实现一个捉迷藏游戏:1.当游戏开始之后,Seeker先把眼睛蒙上,统治Hider;2.Hider接收到通知后,将自己藏起来,再通知Seeker可以找了。

    import threading, time
    
    class Hider(threading.Thread):
        def __init__(self,cond,name):
            super(Hider,self).__init__()  # 需要先执行父类的初始化函数,否则name会被覆盖
            self.cond = cond
            self.name = name
    
        def run(self):
            time.sleep(1)  # 确保Seeker先运行
            self.cond.acquire()  # 3,获得锁,执行下面的操作
            print("我已经把眼睛闭上了!")
            self.cond.notify()  # 通知另一个解锁,自己挂起
            self.cond.wait()
    
            print(self.name,": 我已经找到你了")
            self.cond.notify()
            self.cond.release()
    
            print(self.name,': 我赢了')
    
    
    class Seeker(threading.Thread):
        def __init__(self,cond,name):
            super(Seeker,self).__init__()  # 需要先执行父类的初始化函数,否则name会被覆盖
            self.cond = cond
            self.name = name
    
        def run(self):
            self.cond.acquire()   # 1, 获得锁
            self.cond.wait()      # 2,释放锁的占用,同时线程挂起,知道notify() 并重新占用锁
            print(self.name,": 我已经藏好了,你快点找我把")
            self.cond.notify()
    
            self.cond.wait()
            self.cond.release()
            print(self.name,": 被你找到了,哎")
    
    cond = threading.Condition()
    seeker = Seeker(cond, 'seeker')
    hider = Hider(cond, 'hider')
    seeker.start()
    hider.start()
    
    """
    我已经把眼睛闭上了!
    seeker : 我已经藏好了,你快点找我把
    hider : 我已经找到你了
    hider : 我赢了
    seeker : 被你找到了,哎"""
    

    使用Semaphore和BoundedSemaphore对象

    Python中,可以使用Semaphore和BoundedSemaphore来控制多线程信号系统中的计数器。

    1、Semaphore: 类threading.Semaphore是一个信号机,控制着对公共资源或者临界区的访问,信号机维护着一个计数器,指定可同时访问资源或者进入临界区的线程数,每次

    有一个线程获得信号机,计数器为-1,如计数器为0,其他线程停止访问。

    import threading, time
    
    def fun(semaphore,num):
        semaphore.acquire()
        print("降龙十八掌,发出%d掌"%num)
        time.sleep(3)
        semaphore.release()
    
    if __name__ =='__main__':
        semaphore = threading.Semaphore(2)
    
        for num in range(4):
            t = threading.Thread(target=fun, args=(semaphore,num))
            t.start()  

    执行后,线程0,1是同时运行的,2,3是3秒后运行。

    2、BoundedSemaphore: 它会检查内部计数器的值,并保证它不会大于初始值,如果超过就会引发一个ValueError错误,常用于守护限制访问资源。

    使用Event对象

    在Python中,事件对象是线程间最简单的通信机制之一,线程可以激活在一个事件对象上等待的其他线程。Event对象的实现类是threading.Event,

    这是一个实现事件对象的类,一个event管理一个标志,该标志可以通过set()方法设置为真或通过clear()方法重新设置为假,wait()方法阻塞,直到标志为真,该标志

    初始值为假。

    import threading, time
    
    event = threading.Event()
    
    def func():
        #
        print("%s wait for event ..."%threading.currentThread().getName())
    
        # event.wait(timeout) 阻塞线程,直到event对象内部标实位为True,或超时
        event.wait()
        print("%s recv event."%threading.currentThread().getName())
    
    
    t1 = threading.Thread(target=func)
    t2 = threading.Thread(target=func)
    
    t1.start()
    t2.start()
    time.sleep(2)
    print("MainTread set event")
    event.set() # 将标识位改为True
    
    
    """
    Thread-1 wait for event ...
    Thread-2 wait for event ...
    MainTread set event
    Thread-2 recv event.
    Thread-1 recv event."""
    

      

    使用Timer对象

    Timer:定时器,用于在指定时间后调用一个方法,相应的可以通过cancel() 函数取消相应的timer动作。

    import threading
    def func():
        print("ping")
    
    timer = threading.Timer(5,func)
    timer.start()
  • 相关阅读:
    Bootstrap 2.3.2导航问题
    隐藏wordpress登陆后台
    WordPress 中文图片 上传 自动重命名
    wordpress上传附件提示抱歉,出于安全的考虑,不支持此文件类型
    移动端适配
    项目中常见的正则校验
    vue中 的 this.$nextTick (Vue中DOM的异步更新)
    vue中使用webscoket
    登堂入室---进阶代码
    Flex布局
  • 原文地址:https://www.cnblogs.com/double-W/p/12685267.html
Copyright © 2020-2023  润新知