类 线程安全变量
众所周知,Django有ORM和信号,每次通过ORM对数据库中的记录进行修改,都会触发一些信号(当然还有其他情况也会触发信号,不止是修改表).在实际工作中有这样一种需求,我们需要记录哪个人对哪些记录进行了哪些修改。如果是单线程的话,就很简单了,使用request_started
或者request_finished
信号即可提取出 当前请求的用户。然而实际上,一个服务不可能是单线程的,那么多线程情况下,依旧使用上面两个信号,多个线程修改表(也就是操作ORM中的类),用户肯定是不同的,那么谁又能保证某个用户就是我们想要记录的客户呢?这个时候,就需要确保类中的某个变量是线程安全的。
# pylint:disable-all
import threading
from time import sleep
class Test(object):
name = 'unknown'
thread = threading.local() # 得到的是一个 <_thread._local object at 0x7f1cb57ecf10>
def say(self):
print('my name is: {}'.format(self.name))
print('my thread name is: {}'.format(self.thread.name))
def t(name):
print('set name:{}'.format(name))
Test.name = name
Test.thread.name = name
sleep(3)
Test().say()
def main():
pools = []
for idx in range(2):
pools.append(threading.Thread(target=t, args=('thread{}'.format(idx),))) # 开启两个线程
[x.start() for x in pools] # 分别启动两个线程(就粗略的认为它是启动吧,忽略run方法)
[x.join() for x in pools] # 主线程等待两个子线程的结束
if __name__ == '__main__':
main()
#_______________________________________________________________________________________#
# set name:thread0 # 第一个线程设置 name 和 thread.name 名为 thread0
# set name:thread1 # 第二个线程设置 name 和 thread.name为 thread1
#... 等待 3s
# my name is: thread1 # 第一个线程的 name 变为了 thread1
# my thread name is: thread0 # 第一个线程 thread.name 是 thread0
# my name is: thread1 # 第二个线程的 name 名为 thread1
# my thread name is: thread1 第二个线程的 thread.name 名为 thread1
从上面的打印信息来看,经过多个线程的操作,类的 name
变量发生了变化,而 thread.name 保持了不变。
那么如何在 ORM 中修改呢?
class RuleChangeRecord(models.Model):
thread = threading.local()
rucr_id = models.AutoField(primary_key=True)
rucr_create_time = models.DateTimeField(auto_now_add=True)
rucr_modify_time = models.DateTimeField(auto_now=True)
rucr_user = models.CharField(max_length=255, **blank_null)
rucr_action = models.CharField(max_length=20, choices=consts.ACTION_TYPE_ENUM)
rucr_table_name = models.CharField(max_length=255)
rucr_entry_id = models.IntegerField()
rucr_field_changed = models.TextField(**blank_null)
rucr_old = models.TextField(**blank_null)
rucr_new = models.TextField(**blank_null)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
self.rucr_user = getattr(self.thread, 'user', None)
return super(RuleChangeRecord, self).save(force_insert=force_insert, force_update=force_update,
using=using, update_fields=update_fields)
下面是关于 threading.local()更好,更详细的讲解:
深入理解Python中的ThreadLocal变量(上)
深入理解Python中的ThreadLocal变量(中)
深入理解Python中的ThreadLocal变量(下)