37.3-多线程之thread-local和Timer

2020-01-25  本文已影响0人  BeautifulSoulpy

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

总结:

  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类实现。这并不是说,线程提供了取消的方法。

上一篇 下一篇

猜你喜欢

热点阅读