多线程环境下嵌入式Python与C++变量安全共享方案咨询
问题
我正在将Python集成到C应用中,通过Python脚本读取I2C传感器数据,目标是让Python与C代码并行运行,实现Python变量的持续更新与C++的访问。
我已在C中创建线程运行Python脚本,该脚本通过无限循环每秒获取传感器数据并更新变量,但需要将更新后的变量从Python传递到C供应用其他部分使用。我尝试通过在C++中定义全局atomic变量并从Python脚本更新它,但不确定这是否是最高效或正确的方案。
设备环境:Raspberry Pi 5,Python 3.10
相关代码:
#include <Python.h> #include <iostream> #include <thread> #include <atomic> #include <chrono> // Global variable to store the velocity as an atomic float to ensure thread-safe updates. std::atomic<float> globalVelocity(0.0); void UpdateVelocity() { Py_Initialize(); PyObject* sysPath = PySys_GetObject("path"); PyList_Append(sysPath, PyUnicode_FromString("/path/to/your/module")); PyObject *pName = PyUnicode_DecodeFSDefault("velocity_module"); PyObject *pModule = PyImport_Import(pName); Py_DECREF(pName); if (pModule != NULL) { PyObject *pFunc = PyObject_GetAttrString(pModule, "get_velocity"); if (pFunc && PyCallable_Check(pFunc)) { while (true) { PyObject *pValue = PyObject_CallObject(pFunc, NULL); if (pValue != NULL) { double velocity = PyFloat_AsDouble(pValue); globalVelocity.store(static_cast<float>(velocity)); std::cout << "Updated Velocity: " << velocity << " m/s" << std::endl; Py_DECREF(pValue); } else { PyErr_Print(); std::cerr << "Function call failed" << std::endl; } std::this_thread::sleep_for(std::chrono::seconds(1)); } } else { PyErr_Print(); std::cerr << "Cannot find function 'get_velocity'" << std::endl; } Py_XDECREF(pFunc); Py_DECREF(pModule); } else { PyErr_Print(); std::cerr << "Failed to load 'velocity_module'" << std::endl; } Py_Finalize(); } int main() { std::thread velocityThread(UpdateVelocity); velocityThread.detach(); while (true) { std::cout << "Main thread reading global velocity: " << globalVelocity.load() << " m/s" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); } return 0; }
解决方案
1. 你的atomic全局变量方案是可行且高效的
对于单个浮点型变量来说,std::atomic<float>是线程安全且性能最优的选择:
- 原子操作是硬件级别的同步,不需要内核态的锁开销,在Raspberry Pi 5的ARM架构上支持良好
- 你的代码实现逻辑正确:Python线程调用
get_velocity获取值后,通过store原子写入,主线程用load原子读取,完全避免了数据竞争
小优化点:
- 在单生产者单消费者的场景下,可以指定宽松内存序,比如
globalVelocity.store(val, std::memory_order_relaxed)和globalVelocity.load(std::memory_order_relaxed),进一步降低性能开销 - 避免全局变量:将atomic变量封装到类中,通过指针或引用传递给线程,代码结构更清晰
2. 多变量场景:互斥锁+结构体
如果后续需要共享多个传感器数据(如加速度、角度等),单个atomic无法保证多变量的原子性更新,此时可以用std::mutex配合结构体:
#include <mutex> struct SensorData { float velocity; float acceleration; }; std::mutex sensorMutex; SensorData globalSensorData; // Python线程更新逻辑 std::lock_guard<std::mutex> lock(sensorMutex); globalSensorData.velocity = newVelocity; globalSensorData.acceleration = newAcceleration; // C++主线程读取逻辑 std::lock_guard<std::mutex> lock(sensorMutex); float currentVel = globalSensorData.velocity; float currentAcc = globalSensorData.acceleration;
这种方式确保多个变量的更新是原子性的,不会出现部分变量更新、部分未更新的中间状态。
3. 直接通过Python C API暴露C++对象
如果希望Python脚本直接操作C变量(而非C主动调用Python函数取值),可以把C++变量封装成Python扩展对象:
- 用
PyModule_AddObject将C++的atomic变量或结构体注册到Python模块中 - Python脚本可直接修改该对象,底层自动触发线程安全的更新
这种方式适合Python逻辑复杂、需要主动修改共享数据的场景,代码耦合度更低。
4. 异步队列通信
如果不需要实时同步,允许一定延迟,可以用std::queue配合条件变量实现生产者-消费者模型:
#include <queue> #include <condition_variable> std::queue<float> velocityQueue; std::mutex queueMutex; std::condition_variable cv; // Python线程(生产者)逻辑 std::lock_guard<std::mutex> lock(queueMutex); velocityQueue.push(newVelocity); cv.notify_one(); // C++主线程(消费者)逻辑 std::unique_lock<std::mutex> lock(queueMutex); cv.wait(lock, []{ return !velocityQueue.empty(); }); float latestVel = velocityQueue.back(); // 清空旧数据,只保留最新值 while (velocityQueue.size() > 1) { velocityQueue.pop(); }
这种方式适合传感器数据产生频率高于C++消费频率的场景,避免数据堆积。
总结
- 单变量场景:优先使用你的
std::atomic<float>方案,调整内存序可进一步优化性能 - 多变量场景:用
std::mutex+结构体保证原子性更新 - 需要Python主动修改数据:通过Python C API暴露C++对象
- 异步消费场景:用队列+条件变量实现解耦
内容的提问来源于stack exchange,提问作者user25086481
相关产品推荐
相关产品推荐

