主线程修改lightMode后如何立即终止子线程的循环?
解决方案
先明确原代码的核心问题:
- Work Handler不适合长时间循环:
k_work的处理函数设计用于短周期任务,长时间阻塞会占用系统工作队列线程,影响其他任务执行,应该改用独立线程处理灯光控制逻辑。 - 无同步的共享变量访问:
lightMode和pastLightMode被多线程读写,没有同步机制,会导致数据竞争和未定义行为。 - 阻塞操作无法响应状态变化:
syncFadeLed(3秒)和k_msleep(1秒)都是长阻塞操作,期间无法及时检测lightMode的变化。
针对这些问题,给出基于Zephyr RTOS的具体实现方案:
1. 重构为独立线程 + 事件通知
使用Zephyr的k_event实现主线程对子线程的及时唤醒,同时将共享状态改为原子变量避免数据竞争。
第一步:定义线程和事件变量
在DroneService类中添加:
#include <zephyr/kernel.h> #include <atomic> class DroneService { private: std::atomic<LightModes> lightMode{Stop}; // 原子变量保证线程安全 LightModes pastLightMode{Stop}; struct k_event light_mode_event; // 用于状态变化通知 k_thread light_thread; K_THREAD_STACK_DEFINE(light_thread_stack, 1024); // 线程栈 static void lightControlThread(void* arg); // ...其他成员 };
第二步:初始化线程和事件
在DroneService的构造函数或初始化方法中:
DroneService::DroneService() { k_event_init(&light_mode_event); k_thread_create(&light_thread, light_thread_stack, K_THREAD_STACK_SIZEOF(light_thread_stack), lightControlThread, this, NULL, NULL, K_PRIO_COOP(10), 0, K_NO_WAIT); }
第三步:实现线程逻辑
将原work handler的逻辑迁移到独立线程,用k_poll实现可中断的延迟,确保能及时响应状态变化:
void DroneService::lightControlThread(void* arg) { DroneService* droneService = static_cast<DroneService*>(arg); struct k_poll_event event = K_POLL_EVENT_INITIALIZER( K_POLL_TYPE_EVENT, K_POLL_MODE_NOTIFY_ONLY, &droneService->light_mode_event ); while (true) { LightModes currentMode = droneService->lightMode.load(std::memory_order_acquire); if (currentMode == Stop) { bool stopFade = false; // 从100 fade到10(原3秒,拆分为90步,每步≈33ms) for (int brightness = 100; brightness >= 10 && !stopFade; brightness--) { droneService->rightLed->setBrightness(brightness); // 等待33ms,或被事件唤醒 int ret = k_poll(&event, 1, K_MSEC(33)); if (ret == 0) { k_event_reset(&droneService->light_mode_event); stopFade = true; } currentMode = droneService->lightMode.load(std::memory_order_acquire); if (currentMode != Stop) stopFade = true; } if (stopFade) continue; // 等待1秒,可中断 int ret = k_poll(&event, 1, K_SEC(1)); if (ret == 0) { k_event_reset(&droneService->light_mode_event); continue; } currentMode = droneService->lightMode.load(std::memory_order_acquire); if (currentMode != Stop) continue; // 从10 fade到100(原3秒,拆分为90步) for (int brightness = 10; brightness <= 100 && !stopFade; brightness++) { droneService->rightLed->setBrightness(brightness); int ret = k_poll(&event, 1, K_MSEC(33)); if (ret == 0) { k_event_reset(&droneService->light_mode_event); stopFade = true; } currentMode = droneService->lightMode.load(std::memory_order_acquire); if (currentMode != Stop) stopFade = true; } if (stopFade) continue; // 等待1秒,可中断 ret = k_poll(&event, 1, K_SEC(1)); if (ret == 0) { k_event_reset(&droneService->light_mode_event); } } else if (currentMode == Running) { // 仅在模式切换时执行一次fade std::lock_guard<std::mutex> lock(droneService->mode_mutex); // 保护pastLightMode if (droneService->pastLightMode != Running) { LOG_DBG("*************Running\n"); droneService->pastLightMode = Running; droneService->rightLed->syncFadeLed(10, 100, 500); } // 等待状态变化事件 k_event_wait(&droneService->light_mode_event, K_FOREVER); k_event_reset(&droneService->light_mode_event); } else { // 其他模式:等待状态变化 k_event_wait(&droneService->light_mode_event, K_FOREVER); k_event_reset(&droneService->light_mode_event); } } }
第四步:修改状态设置方法
主线程设置模式后,通过事件通知子线程:
void DroneService::setUpLightsMode(LightModes mode){ lightMode.store(mode, std::memory_order_release); k_event_post(&light_mode_event); }
2. 关键优化点说明
- 原子变量:
lightMode用std::atomic修饰,保证多线程读写的原子性,避免数据竞争。 - 可中断延迟:用
k_poll替代k_msleep,既能实现定时,又能在状态变化时被立即唤醒。 - 拆分阻塞操作:将长时长的
syncFadeLed拆分为逐帧调整亮度的循环,每帧都检查状态,确保能快速响应模式切换。 - 独立线程:避免占用系统工作队列,保证其他任务正常执行。
备选方案:使用互斥锁 + 条件变量(标准C++)
如果不依赖Zephyr的API,也可以用标准C++的std::mutex和std::condition_variable实现:
#include <mutex> #include <condition_variable> class DroneService { private: std::mutex mode_mutex; std::condition_variable mode_cv; LightModes lightMode{Stop}; LightModes pastLightMode{Stop}; // ... }; // 主线程设置模式 void DroneService::setUpLightsMode(LightModes mode){ std::lock_guard<std::mutex> lock(mode_mutex); lightMode = mode; mode_cv.notify_one(); } // 子线程逻辑片段(替换k_poll部分) std::unique_lock<std::mutex> lock(droneService->mode_mutex); // 等待1秒或被通知 if (droneService->mode_cv.wait_for(lock, std::chrono::seconds(1), [droneService](){ return droneService->lightMode != Stop; })) { // 模式已变化,退出循环 continue; }
内容的提问来源于stack exchange,提问作者Rubén Méndez Vallés
相关产品推荐
相关产品推荐

