zoukankan      html  css  js  c++  java
  • Python线程同步

    线程执行

    join与setDaemon

    子线程在主线程运行结束后,会继续执行完,如果给子线程设置为守护线程(setDaemon=True),主线程运行结束子线程即结束;

    如果join()线程,那么主线程会等待子线程执行完再执行。

     1 import threading
     2 import time
     3 
     4 
     5 def get_thread_a():
     6     print("get thread A started")
     7     time.sleep(3)
     8     print("get thread A end")
     9 
    10 
    11 def get_thread_b():
    12     print("get thread B started")
    13     time.sleep(5)
    14     print("get thread B end")
    15 
    16 
    17 if  __name__ == "__main__":
    18     thread_a = threading.Thread(target=get_thread_a)
    19     thread_b = threading.Thread(target=get_thread_b)
    20     start_time = time.time()
    21     thread_b.setDaemon(True)
    22     thread_a.start()
    23     thread_b.start()
    24     thread_a.join()
    25     
    26     end_time = time.time()
    27     print("execution time: {}".format(end_time - start_time))
    thread_a是join,首先子线程thread_a执行,thread_b是守护线程,当主线程执行完后,thread_b不会再执行
    执行结果如下:
    get thread A started
    get thread B started
    get thread A end
    execution time: 3.003199815750122
    
    

    线程同步

    当线程间共享全局变量,多个线程对该变量执行不同的操作时,该变量最终的结果可能是不确定的(每次线程执行后的结果不同),如:对count变量执行加减操作 ,count的值是不确定的,要想count的值是一个确定的需对线程执行的代码段加锁。

    python对线程加锁主要有Lock和Rlock模块

    Lock:

    from threading import Lock
    
    lock = Lock()
    lock.acquire()
    lock.release()
    

    Lock有acquire()和release()方法,这两个方法必须是成对出现的,acquire()后面必须release()后才能再acquire(),否则会造成死锁

    Rlock:

    鉴于Lock可能会造成死锁的情况,RLock(可重入锁)对Lock进行了改进,RLock可以在同一个线程里面连续调用多次acquire(),但必须再执行相同次数的release()

    from threading import RLock
    
    lock = RLock()
    lock.acquire()
    lock.acquire()
    lock.release()
    lock.release()

    condition(条件变量),线程在执行时,当满足了特定的条件后,才可以访问相关的数据

    import threading
    
    
    def get_thread_a(condition):
        with condition:
            condition.wait()
            print("A : Hello B,that's ok")
            condition.notify()
            condition.wait()
            print("A : I'm fine,and you?")
            condition.notify()
            condition.wait()
            print("A : Nice to meet you")
            condition.notify()
            condition.wait()
            print("A : That's all for today")
            condition.notify()
    
    
    def get_thread_b(condition):
        with condition:
            print("B : Hi A, Let's start the conversation")
            condition.notify()
            condition.wait()
            print("B : How are you")
            condition.notify()
            condition.wait()
            print("B : I'm fine too")
            condition.notify()
            condition.wait()
            print("B : Nice to meet you,too")
            condition.notify()
            condition.wait()
            print("B : Oh,goodbye")
    
    
    if __name__ == "__main__":
        condition = threading.Condition()
        thread_a = threading.Thread(target=get_thread_a, args=(condition,))
        thread_b = threading.Thread(target=get_thread_b, args=(condition,))
        thread_a.start()
        thread_b.start()

    Condition内部有一把锁,默认是RLock,在调用wait()和notify()之前必须先调用acquire()获取这个锁,才能继续执行;当wait()和notify()执行完后,需调用release()释放这个锁,在执行with condition时,会先执行acquire(),with结束时,执行了release();所以condition有两层锁,最底层锁在调用wait()时会释放,同时会加一把锁到等待队列,等待notify()唤醒释放锁

    wait() :允许等待某个条件变量的通知,notify()可唤醒

    notify(): 唤醒等待队列wait()

    执行结果:

    B : Hi A, Let's start the conversation
    A : Hello B,that's ok
    B : How are you
    A : I'm fine,and you?
    B : I'm fine too
    A : Nice to meet you
    B : Nice to meet you,too
    A : That's all for today
    B : Oh,goodbye

    Semaphore(信号量)

     用于控制线程的并发数,如爬虫中请求次数过于频繁会被禁止ip,每次控制爬取网页的线程数量可在一定程度上防止ip被禁;文件读写中,控制写线程每次只有一个,读线程可多个。

    import time
    import threading
    
    
    def get_thread_a(semaphore,i):
        time.sleep(1)
        print("get thread : {}".format(i))
        semaphore.release()
    
    
    def get_thread_b(semaphore):
        for i in range(10):
            semaphore.acquire()
            thread_a = threading.Thread(target=get_thread_a, args=(semaphore,i))
            thread_a.start()
    
    
    if __name__ == "__main__":
        semaphore = threading.Semaphore(2)
        thread_b = threading.Thread(target=get_thread_b, args=(semaphore,))
        thread_b.start()

    上述示例了每隔1秒并发两个线程执行的情况,当调用一次semaphore.acquire()时,Semaphore的数量就减1,直至Semaphore数量为0时被锁上,当release()后Semaphore数量加1。Semaphore在本质上是调用的Condition,semaphore.acquire()在Semaphore的值为0的条件下会调用Condition.wait(), 否则将值减1,semaphore.release()会将Semaphore的值加1,并调用Condition.notify()

    Semaphore源码

    def acquire(self, blocking=True, timeout=None):
            if not blocking and timeout is not None:
                raise ValueError("can't specify timeout for non-blocking acquire")
            rc = False
            endtime = None
            with self._cond:
                while self._value == 0:
                    if not blocking:
                        break
                    if timeout is not None:
                        if endtime is None:
                            endtime = _time() + timeout
                        else:
                            timeout = endtime - _time()
                            if timeout <= 0:
                                break
                    self._cond.wait(timeout)
                else:
                    self._value -= 1
                    rc = True
            return rc
    
    def release(self):
            with self._cond:
                self._value += 1
                self._cond.notify()
  • 相关阅读:
    saltstack总结-2018-0620
    我的书籍《Redis 源码日志》
    深入剖析 redis 主从复制
    深入剖析 redis AOF 持久化策略
    初探单点登录 SSO
    深入剖析 redis RDB 持久化策略
    深入剖析 redis 事件驱动
    memcached 源码阅读笔记
    Django 源码小剖: Django ORM 查询管理器
    Django 源码小剖: Django 对象关系映射(ORM)
  • 原文地址:https://www.cnblogs.com/FG123/p/9704158.html
Copyright © 2011-2022 走看看