美文网首页
37.3-多线程之thread-local和Timer

37.3-多线程之thread-local和Timer

作者: BeautifulSoulpy | 来源:发表于2020-01-25 06:59 被阅读0次

    一个人的状态挺好的,想看书了就看书,累了就睡觉,想吃啥就吃啥,不想联系谁就自己安静一阵,出去旅行或是宅在家怎么都好。对爱情最好还是保持点儿洁癖,不要随便开始,不要急着妥协,真正值得的东西都不会那么轻易。珍惜还可以单身的日子吧,“不介意孤独,比爱你舒服”!

    总结:

    1. 线程运行中的唯一:线程ID、线程内存地址;

    1. threading.local类

    下例使用多线程,每个线程完成不同的计算任务。
    x是局部变量,可以看出每一个线程的x是独立的,互不干扰的,为什么?
    能否改造成使用全局变量完成。

    import threading
    import time
    import logging
    
    FORMAT = "%(asctime)s %(threadName)s %(message)s"
    logging.basicConfig(format=FORMAT,level=logging.INFO)
    
    # 局部变量实现
    def worker():
        x = 0
        for _ in range(100):
            time.sleep(0.0001)
            x += 1
        logging.info(x)
    
    for i in range(10):
        t = threading.Thread(target=worker,name='w-{}'.format(i))
        t.start()
    
    print("===============================")
    #---------------------------------------------------------------------
    ===============================
    2020-01-13 19:24:49,849 w-5 100
    2020-01-13 19:24:49,849 w-1 100
    2020-01-13 19:24:49,849 w-6 100
    2020-01-13 19:24:49,849 w-4 100
    2020-01-13 19:24:49,849 w-3 100
    2020-01-13 19:24:49,849 w-7 100
    2020-01-13 19:24:49,849 w-2 100
    2020-01-13 19:24:49,849 w-0 100
    2020-01-13 19:24:49,851 w-9 100
    2020-01-13 19:24:49,851 w-8 100
    
    
    # 全局实现方式1
    import threading
    import time
    import logging
    
    FORMAT = "%(asctime)s %(threadName)s %(message)s"
    logging.basicConfig(format=FORMAT,level=logging.INFO)
    
    x = 0
    def worker():
        #x = 0
        global x
        for _ in range(100):
            time.sleep(0.0001)
            x += 1
        logging.info(x)
    
    for i in range(5):
        t = threading.Thread(target=worker,name='w-{}'.format(i))
        t.start()
    
    print("===============================")
    #-------------------------------------------------------------------------------------------
    ===============================
    2020-01-13 19:30:49,065 w-1 489
    2020-01-13 19:30:49,067 w-3 494
    2020-01-13 19:30:49,067 w-2 495
    2020-01-13 19:30:49,070 w-4 498
    2020-01-13 19:30:49,072 w-0 500
    
    # 全局实现方式2
    import threading
    import time
    import logging
    
    FORMAT = "%(asctime)s %(threadName)s %(message)s"
    logging.basicConfig(format=FORMAT,level=logging.INFO)
    
    class A:
        def __init__(self):
            self.x = 0
            
    a = A()
    def worker():
        #x = 0
        #global x
        for _ in range(100):
            time.sleep(0.0001)
            a.x += 1
        logging.info(a.x)
    
    for i in range(5):
        t = threading.Thread(target=worker,name='w-{}'.format(i))
        t.start()
    
    print("===============================")
    

    上例虽然使用了全局对象,但是线程之间互相干扰,导致了不期望的结果。
    能不能既使用全局对象,还能保持每个线程使用不同的数据呢?

    python提供 threading.local 类,将这个类实例化得到一个全局对象,但是不同的线程使用这个对象存储的数据,其他线程看不见。

    import threading
    import time
    import logging
    
    FORMAT = "%(asctime)s %(threadName)s %(message)s"
    logging.basicConfig(format=FORMAT,level=logging.INFO)
    
    class A:
        def __init__(self):
            self.x = 0
    
    a = threading.local()
    #a = A()
    
    def worker():
        #x = 0
        #global x
        a.x = 0
        for _ in range(100):
            time.sleep(0.0001)
            a.x += 1
        logging.info(a.x)
    
    for i in range(5):
        t = threading.Thread(target=worker,name='w-{}'.format(i))
        t.start()
    
    print("===============================")
    #----------------------------------------------------------------------------------------
    ===============================
    2020-01-13 19:54:59,524 w-2 100
    2020-01-13 19:54:59,524 w-3 100
    2020-01-13 19:54:59,525 w-0 100
    2020-01-13 19:54:59,525 w-4 100
    2020-01-13 19:54:59,526 w-1 100
    
    

    结果显示和使用局部变量的效果一样。 再看threading.local的例子

    import threading
    
    X = 'abc'
    ctx = threading.local() # 注意这个对象所处的线程
    ctx.x = 123
    print(ctx, type(ctx), ctx.x)
    
    def worker():
        print(X)
        print(ctx)
        print(ctx.x)
        print('working')
    
    worker() # 普通函数调用
    print()
    threading.Thread(target=worker).start() # 另起一个线程;
    #---------------------------------------------------------------------------------
    
    

    从运行结果来看,另起一个线程打印ctx.x出错了。

    AttributeError: '_thread._local' object has no attribute 'x'
    

    但是,ctx打印没有出错,说明看到ctx,但是ctx中的x看不到,这个x不能跨线程。

    threading.local类构建了一个大字典,存放所有线程相关的字典,定义如下:
    { id(Thread) -> (ref(Thread), thread-local dict) }
    每一线程实例的id为key,元组为value。
    value中2部分为,线程对象引用,每个线程自己的字典。

    本质
    运行时,threading.local实例处在不同的线程中,就从大字典中找到当前线程相关键值对中的字典,覆盖
    threading.local实例的 dict
    这样就可以在不同的线程中,安全地使用线程独有的数据,做到了线程间数据隔离,如同本地变量一样安
    全。

    2. 定时器 Timer/延迟执行

    threading.Timer继承自Thread,这个类用来定义延迟多久后执行一个函数;

    本质上:还是启动了一个线程执行;
    class threading.Timer(interval, function, args=None, kwargs=None)
    

    start方法执行之后,Timer对象会处于等待状态,等待了interval秒之后,开始执行function函数的;

    import threading
    import logging
    import time
    
    FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
    logging.basicConfig(level=logging.INFO, format=FORMAT)
    
    def worker():
        logging.info('in worker')
        time.sleep(2)
    
    t = threading.Timer(4, worker)
    t.setName('timer')
    
    # t.cancel()
    t.start()
    t.cancel()
    while True:
        print(threading.enumerate())
        time.sleep(1)
    #--------------------------------------------------------------------------------------------
    
    

    Timer提供了cancel方法,用来取消一个未执行的函数,如果上面例子中worker函数已经开始执行,cancel就没有任何效果了;

    总结
    Timer是线程Thread的子类,就是线程类,具有线程的能力和特征;
    它的实例是能够延时执行目标函数的线程,在真正执行目标函数之前,都可以cancel它。
    cancel方法本质使用Event类实现。这并不是说,线程提供了取消的方法。

    相关文章

      网友评论

          本文标题:37.3-多线程之thread-local和Timer

          本文链接:https://www.haomeiwen.com/subject/ltcnactx.html