《lvgl基础学习 —— 多线程》
1.为什么要关注lvgl里的多线程
LVGL 不是线程安全的。
任何 LVGL API,只能在“UI 线程”里调用。
允许的:
-
多线程做 I/O、算法、驱动
-
多线程通过消息通知 UI
禁止的:
-
在子线程里
lv_label_set_text -
在子线程里
lv_scr_load -
在子线程里创建/删除对象
👉 违反一次,迟早随机崩溃
原因不是“LVGL 不行”,而是:
-
LVGL 内部:
-
单链表
-
全局状态
-
无锁设计(为了快)
-
-
加锁会严重影响性能(尤其嵌入式)
👉 官方选择:单线程 UI + 外部并发
2.LVGL中正确的多线程模型(建议都这么用)
┌──────────────┐ │ UI Thread │ ← lv_timer_handler() │ (LVGL only) │ ← 所有 lv_* API └──────▲───────┘ │ 消息 ┌──────┴───────┐ │ Worker Thread│ ← 硬件 / 网络 / 算法 │ (no LVGL) │ └──────────────┘
一句话:
子线程只“算/读/等”,
UI 线程只“画/显示/交互”。
3.三种”线程 <-----> UI“通信方式
方式1:消息队列 + UI轮询
子线程:
post_msg(MSG_UPDATE_TEXT, value);
UI线程:
while(fetch_msg(&msg)) { lv_label_set_text_fmt(label, "%d", msg.value); }
优点:简单、稳定、不用锁LVGL
方式2:lv_async_call()(LVGL 官方接口)
让子线程”请求UI线程执行一个函数“
demo:
static void ui_update_cb(void *data) { int val = (int)data; lv_label_set_text_fmt(label, "%d", val); } /* 子线程 */ lv_async_call(ui_update_cb, (void *)value);
注意:
-
回调在 UI 线程 执行
-
不适合高频调用(比如 100Hz)
方式3:直接加muxer锁LVGL(不推荐)
pthread_mutex_lock(&lvgl_lock); lv_label_set_text(label, "xxx"); pthread_mutex_unlock(&lvgl_lock);
很容易死锁
官方不推荐
实际工程很难维护
4.完整多线程demo
场景
-
UI:显示一个数值
-
子线程:每 1 秒递增
-
UI 不卡
1.消息定义
typedef enum { MSG_UPDATE_VALUE, } msg_type_t; typedef struct { msg_type_t type; int value; } app_msg_t;
2.子线程(woker.c)
#include <pthread.h> #include <unistd.h> #include "app_msg.h" static void *worker_thread(void *arg) { int val = 0; while(1) { sleep(1); val++; app_msg_t msg = { .type = MSG_UPDATE_VALUE, .value = val, }; app_msg_post(&msg); } return NULL; } void worker_start(void) { pthread_t tid; pthread_create(&tid, NULL, worker_thread, NULL); }
3.ui层(ui.c)
#include "lvgl.h" #include "app_msg.h" static lv_obj_t *label; void ui_counter_init(void) { label = lv_label_create(lv_scr_act()); lv_label_set_text(label, "Value: 0"); lv_obj_center(label); } void ui_counter_process_msg(app_msg_t *msg) { if(msg->type == MSG_UPDATE_VALUE) { lv_label_set_text_fmt(label, "Value: %d", msg->value); } }
4.main.c
int main(void) { lv_init(); hal_init(); app_msg_init(); ui_counter_init(); worker_start(); app_msg_t msg; while(1) { lv_timer_handler(); while(app_msg_fetch(&msg) == 0) { ui_counter_process_msg(&msg); } usleep(5000); } }
5.lv_timer VS 线程(区分清楚)
❌ 错误理解
“我用线程,其实是为了定时”
✅ 正确用法
-
UI 定时:
lv_timer_create() -
后台任务:线程
lv_timer_create(ui_refresh_cb, 1000, NULL);
不要用线程干UI定时的活。
最常见的致命坑(一定要避)
❌ 子线程调用任何 lv_*
❌ 消息里传指针(生命周期错)
❌ UI 线程 sleep 很久
❌ worker 线程里 printf 太多(卡)
❌ 多线程同时改全局状态
五、一个“完整可跑”的多线程 Demo(强烈建议照着做)
场景
-
UI:显示一个数值
-
子线程:每 1 秒递增
-
UI 不卡
浙公网安备 33010602011771号