Flask中的ThreadLocal本地线程,上下文管理

本文介绍了如何使用ThreadLocal解决多线程环境中数据冲突问题,并通过代码示例展示了不同场景下ThreadLocal的实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先说一下和flask没有关系的:

我们都知道线程是由进程创建出来的,CPU实际执行的也是线程,那么线程其实是没有自己独有的内存空间的,所有的线程共享进程的资源和空间,共享就会有冲突,对于多线程对同一块数据处理的冲突问题,一个办法就是加互斥锁,另一个办法就是利用threadlocal

ThreadLocal   实现的思路就是给一个进程中的多个线程开辟空间来保存线程中特有的值

代码实现:

1、简单示例:

import threading
# 实例化对象
local_values = threading.local()
def func(num):
    # 给对象加属性,这个属性就会保存在当前线程开辟的空间中
    local_values.name = num
    import time
    time.sleep(1)
    # 取值的时候也从当前线程开辟的空间中取值
    print(local_values.name, threading.current_thread().name)
for i in range(20):
    th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
    th.start()

打印结果:

0 线程0
1 线程1
2 线程2
3 线程3
10 线程10
9 线程9
4 线程4
8 线程8
5 线程5
7 线程7
6 线程6
13 线程13
11 线程11
17 线程17
15 线程15
14 线程14
16 线程16
12 线程12
18 线程18
19 线程19

 

如果把对象换成一个类对象:

import threading
# 如果是一个类对象,结果就完全不一样
 class Foo(object):
     def __init__(self):
         self.name = 0
local_values = Foo()
def func(num):
    local_values.name = num
    import time
    time.sleep(1)
    print(local_values.name, threading.current_thread().name)
for i in range(20):
    th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
    th.start()

打印结果:

19 线程1
19 线程3
19 线程4
19 线程5
19 线程2
19 线程0
19 线程9
19 线程6
19 线程8
19 线程11
19 线程7
19 线程10
19 线程15
19 线程16
19 线程13
19 线程14
19 线程18
19 线程19
19 线程12
19 线程17

 

2、依据这个思路,我们自己实现给线程开辟独有的空间保存特有的值

协程和线程都有自己的唯一标识get_ident,利用这个唯一标识作为字典的key,key对应的value就是当前线程或协程特有的值,取值的时候也拿这个key来取

import threading
# get_ident就是获取线程或协程唯一标识的
try:
    from greenlet import getcurrent as get_ident # 协程
    # 当没有协程的模块时就用线程
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident # 线程
class Local(object):
    def __init__(self):
        self.storage = {}
        self.get_ident = get_ident
    def set(self,k,v):
        ident = self.get_ident()
        origin = self.storage.get(ident)
        if not origin:
            origin = {k:v}
        else:
            origin[k] = v
        self.storage[ident] = origin
    def get(self,k):
        ident = self.get_ident()
        origin = self.storage.get(ident)
        if not origin:
            return None
        return origin.get(k,None)
# 实例化自定义local对象对象
local_values = Local()
def task(num):
    local_values.set('name',num)
    import time
    time.sleep(1)
    print(local_values.get('name'), threading.current_thread().name)
for i in range(20):
    th = threading.Thread(target=task, args=(i,),name='线程%s' % i)
    th.start()

 

 

3、因为要不停的赋值取值,就想到了__setattr__和__getattr__方法,但是要注意初始化时赋值和__setattr__方法中赋值又可能产生递归的问题

import threading
try:
    from greenlet import getcurrent as get_ident # 协程
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident # 线程
class Local(object):
    def __init__(self):
        # 这里一定要用object来调用,因为用self调用的就会触发__setattr__方法,__setattr__方法里
        # 又会用self去赋值就又会调用__setattr__方法,就变成递归了
        object.__setattr__(self, '__storage__', {})
        object.__setattr__(self, '__ident_func__', get_ident)
    def __getattr__(self, name):
        try:
            return self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)
    def __setattr__(self, name, value):
        ident = self.__ident_func__()
        storage = self.__storage__
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}
    def __delattr__(self, name):
        try:
            del self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)
local_values = Local()
def task(num):
    local_values.name = num
    import time
    time.sleep(1)
    print(local_values.name, threading.current_thread().name)
for i in range(20):
    th = threading.Thread(target=task, args=(i,),name='线程%s' % i)
    th.start()

 

 

我们再说回Flask,我们知道django中的request是直接当参数传给视图的,这就不存在几个视图修改同一个request的问题,但是flask不一样,flask中的request是导入进去的,就相当于全局变量,每一个视图都可以对它进行访问修改取值操作,这就带来了共享数据的冲突问题,解决的思路就是我们上边的第三种代码,利用协程和线程的唯一标识作为key,也是存到一个字典里,类中也是采用__setattr__和__getattr__方法来赋值和取值

分情况:

对于单进程单线程:没有影响,基于全局变量

对于单进程多线程:利用threading.local()  对象

对于单进程单线程的多协程:本地线程对象就做不到了,要用自定义,就是上边的第三个代码

 

一共涉及到四个知识点:

1、唯一标识

2、本地线程对象

3、setattr和getattr

4、怎么实现

转载于:https://www.cnblogs.com/wanghl1011/articles/8619148.html

/* * Copyright (c) 2024 iSoftStone Education Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <stdio.h> #include <stdbool.h> #include "los_task.h" #include "ohos_init.h" #include "cmsis_os.h" #include "config_network.h" #include "sht30.h" #include "smart_security.h" #include "smart_home_event.h" #include "su_03t.h" #include "iot.h" #include "picture.h" #include "components.h" #include "drv_light.h" #include "lcd.h" #include "adc_key.h" #include "mq2.h" #include "mq21.h" #include "mq22.h" #define ROUTE_SSID "扣1拯救世界" #define ROUTE_PASSWORD "111111111" #define MSG_QUEUE_LENGTH 16 #define BUFFER_LEN 50 #define Level_Alert_1 10 #define Level_Alert_2 20 #define Level_Alert_3 30 static unsigned int m_msg_queue; unsigned int m_su03_msg_queue; unsigned int mq2_gas_queue; bool alarm_light_state = false; bool beep_state = false; int num = 1; /*************************************************************** * 函数名称: iot_thread * 说 明: iot线程 * 参 数: 无 * 返 回 值: 无 ***************************************************************/ void iot_thread(void *args) { uint8_t mac_address[6] = {0x00, 0xdc, 0xb6, 0x90, 0x00, 0x00}; FlashInit(); VendorSet(VENDOR_ID_WIFI_MODE, "STA", 3); // 配置为Wifi STA模式 VendorSet(VENDOR_ID_MAC, mac_address, 6); // 多人同时做该实验,请修改各自不同的WiFi MAC地址 VendorSet(VENDOR_ID_WIFI_ROUTE_SSID, ROUTE_SSID, sizeof(ROUTE_SSID)); VendorSet(VENDOR_ID_WIFI_ROUTE_PASSWD, ROUTE_PASSWORD, sizeof(ROUTE_PASSWORD)); reconnect: SetWifiModeOff(); SetWifiModeOn(); mqtt_init(); while (1) { if (!wait_message()) { goto reconnect; } LOS_Msleep(1); } } /*************************************************************** * 函数名称: smart_security_thread * 说 明: 智慧安防主线程 * 参 数: 无 * 返 回 值: 无 ***************************************************************/ void smart_security_thread(void *arg) { double *data_ptr = NULL; unsigned int duty = 0; double ppm_range0 = 2000; double ppm_range1 = 10000; double ppm_range2 = 40000; double ppm_range = 300; e_iot_data iot_data = {0}; bool light_last_state = alarm_light_state; bool beep_last_state = beep_state; double temperature_range = 35.0; double humidity_range = 80.0; beep_dev_init(); alarm_light_init(); lcd_dev_init(); //su03t_init(); lcd_load_ui(); while(1) { event_info_t event_info = {0}; //等待事件触发,如有触发,则立即处理对应事件,如未等到,则执行默认的代码逻辑,更新屏幕 int ret = smart_home_event_wait(&event_info,3000); if(ret == LOS_OK){ //收到指令 //lcd_show_chinese(0,28,"甲烷",LCD_BLACK,LCD_WHITE,24,0); printf("event recv %d ,%d\n",event_info.event,event_info.data.iot_data); switch (event_info.event) { case event_key_press: smart_home_key_process(event_info.data.key_no); break; default:break; } } if(event_info.data.key_no == KEY_DOWN){ break; } lcd_load_ui(); } num = lcd_gas_kind(); lcd_dev_init(); lcd_show_ui(); while(1) { LOS_QueueRead(m_msg_queue, (void *)&data_ptr, BUFFER_LEN, LOS_WAIT_FOREVER); if (data_ptr[0] > ppm_range0||data_ptr[2] > ppm_range) { duty = Level_Alert_1; if(data_ptr[0]>ppm_range2) { duty = Level_Alert_3; } if(data_ptr[0]>ppm_range1) { duty = Level_Alert_2; } alarm_light_state = true; beep_state = true; beep_set_state(true, duty); alarm_light_set_gpio(true); light_last_state = alarm_light_state; beep_last_state = beep_state; } duty = Level_Alert_1; beep_set_state(beep_state, duty); alarm_light_set_gpio(alarm_light_state); light_last_state = alarm_light_state; beep_last_state = beep_state; lcd_set_temperature(data_ptr[3]); lcd_set_humidity(data_ptr[4]); lcd_set_gas(data_ptr[0]); lcd_set_ppm(data_ptr[1]); lcd_set_body_induction((bool)data_ptr[2]); lcd_set_beep_state(beep_state); lcd_set_alarm_light_state(alarm_light_state); //lcd_set_auto_state(auto_state); lcd_show_ui(); if (mqtt_is_connected()) { iot_data.gas = data_ptr[0]; iot_data.smoke = data_ptr[1]; iot_data.body = (bool)data_ptr[2]; iot_data.temp = data_ptr[3]; iot_data.humidity = data_ptr[4]; iot_data.beep_state = beep_state; iot_data.alarm_light_state = alarm_light_state; send_msg_to_mqtt(&iot_data); } printf("============= smart security example ==============\n"); printf("======== data ========\r\n"); printf("ppm:%5.2f\r\n", data_ptr[0]); printf("body_induction:%5.2f\r\n", data_ptr[1]); printf("======== state ========\r\n"); printf("beep_state:%d\r\n", beep_state); printf("alarm_light_state:%d\r\n", alarm_light_state); printf("\r\n"); LOS_Msleep(500); } } /*************************************************************** * 函数名称: device_read_thraed * 说 明: 设备读取线程 * 参 数: 无 * 返 回 值: 无 ***************************************************************/ void device_read_thraed(void *arg) { double read_data[5] = {0}; double *read_gas; body_induction_dev_init(); sht30_init();//温湿度传感器初始化 while(1) { LOS_QueueRead(mq2_gas_queue, (void *)&read_gas, BUFFER_LEN, LOS_WAIT_FOREVER); read_data[0] = read_gas[0]; //printf("1 %lf", read_data[0]); read_data[1] = read_gas[1]; body_induction_get_state((bool *)&read_data[2]); sht30_read_data(&read_data[3]);//读取sht30数据 LOS_QueueWrite(m_msg_queue, (void *)&read_data, sizeof(read_data), LOS_WAIT_FOREVER); LOS_QueueWrite(m_su03_msg_queue, (void *)&read_data, sizeof(read_data), LOS_WAIT_FOREVER); LOS_Msleep(500); } } void mq2_thread(void *arg) { //初始化 mq2_dev_init(); mq2_ppm_calibration(); //获取温湿度 double dat[2]; double gas_ppm[2] = {0}; float r0_1; LOS_QueueWrite(mq2_gas_queue, (void *)&gas_ppm, sizeof(gas_ppm), LOS_WAIT_FOREVER); //48小时预热 printf("预热开始,需要48小时...\n"); for(int i = 0; i < 1; i++) { sleep(1); if(i % 3600 == 0) { printf("已预热 %d 小时...\n", i/3600); } } printf("预热完成!\n"); if(!num) { r0_1 = env_set_1(); } else if(num) { env_set(); } while(1) { if (!num) { gas_ppm[0] = mq21_ppm(r0_1); } else if(num) { gas_ppm[0] = mq22_ppm(); printf(" 3:%lf 4:%lf",mq22_ppm(),gas_ppm[0]); } gas_ppm[1] = get_mq2_ppm(); LOS_QueueWrite(mq2_gas_queue, (void *)&gas_ppm, sizeof(gas_ppm), LOS_WAIT_FOREVER); } } /*************************************************************** * 函数名称: smart_security_example * 说 明: 开机自启动调用函数 * 参 数: 无 * 返 回 值: 无 ***************************************************************/ void iot_smart_security_example() { unsigned int thread_id_1; unsigned int thread_id_2; unsigned int thread_id_3; unsigned int thread_id_4; TSK_INIT_PARAM_S task_1 = {0}; TSK_INIT_PARAM_S task_2 = {0}; TSK_INIT_PARAM_S task_3 = {0}; TSK_INIT_PARAM_S task_4 = {0}; unsigned int ret = LOS_OK; ret = LOS_QueueCreate("queue", MSG_QUEUE_LENGTH, &m_msg_queue, 0, BUFFER_LEN); if (ret != LOS_OK) { printf("Falied to create Message Queue ret:0x%x\n", ret); return; } ret = LOS_QueueCreate("su03_queue", MSG_QUEUE_LENGTH, &m_su03_msg_queue, 0, BUFFER_LEN); if (ret != LOS_OK) { printf("Falied to create Message Queue ret:0x%x\n", ret); return; } task_1.pfnTaskEntry = (TSK_ENTRY_FUNC)smart_security_thread; task_1.uwStackSize = 2048; task_1.pcName = "smart security thread"; task_1.usTaskPrio = 24; ret = LOS_TaskCreate(&thread_id_1, &task_1); if (ret != LOS_OK) { printf("Falied to create task ret:0x%x\n", ret); return; } task_2.pfnTaskEntry = (TSK_ENTRY_FUNC)device_read_thraed; task_2.uwStackSize = 2048; task_2.pcName = "device read thraed"; task_2.usTaskPrio = 24; ret = LOS_TaskCreate(&thread_id_2, &task_2); if (ret != LOS_OK) { printf("Falied to create task ret:0x%x\n", ret); return; } task_3.pfnTaskEntry = (TSK_ENTRY_FUNC)iot_thread; task_3.uwStackSize = 20480; task_3.pcName = "iot thread"; task_3.usTaskPrio = 24; ret = LOS_TaskCreate(&thread_id_3, &task_3); if (ret != LOS_OK) { printf("Falied to create task ret:0x%x\n", ret); return; } unsigned int thread_id0; TSK_INIT_PARAM_S task0 = {0}; unsigned int ret0 = LOS_OK; task0.pfnTaskEntry = (TSK_ENTRY_FUNC)mq2_thread; task0.uwStackSize = 2048; task0.pcName = "mq2 thread"; task0.usTaskPrio = 24; ret0 = LOS_TaskCreate(&thread_id0, &task0); if (ret0 != LOS_OK) { printf("Falied to create task ret:0x%x\n", ret0); return; } ret0 = LOS_QueueCreate("gas_queue", MSG_QUEUE_LENGTH, &mq2_gas_queue, 0, BUFFER_LEN); if (ret0 != LOS_OK) { printf("Falied to create Message Queue ret:0x%x\n", ret0); return; } task_4.pfnTaskEntry = (TSK_ENTRY_FUNC)adc_key_thread; task_4.uwStackSize = 2048; task_4.pcName = "key thread"; task_4.usTaskPrio = 24; ret = LOS_TaskCreate(&thread_id_4, &task_4); if (ret != LOS_OK) { printf("Falied to create task ret:0x%x\n", ret); return; } } APP_FEATURE_INIT(iot_smart_security_example); 检查这段代码为什么接收不到事件触发
最新发布
06-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值