对于嵌入式系统项目,我必须处理同时发送大量数据的传感器。目前,每个传感器都有自己的线程,并且许多线程相互引用。
使用互斥锁,这些线程从彼此获取数据。但是,在生产过程中,某些线程无限期地等待另一个线程以完成对锁定数据的处理。我知道这个问题与僵局有关,但我发现这些问题很难发现和预防。
我想避免如此广泛地使用互斥体,因为它们会导致我大部分难以重现的问题。我已经尝试了很多方法,例如在互斥锁超出范围时自动解锁互斥锁,但到目前为止没有任何效果。 我有一个包含以下方法的共享数据类:
```
template<class T>
T SharedData<T>::Get() {
LockGuard lock(mutex_);
T data = data_;
if (!IsValid() && has_default_value_) {
data = default_value_;
}
return data;
}
template<class T>
void SharedData<T>::Set(T data) {
is_set_ = true;
set_time_ = system_clock::now();
LockGuard lock(mutex_);
data_ = data;
}
```
我的问题如下;在线程之间共享实时数据的好方法是什么(最好不使用互斥锁(?
我正在寻找线程之间传递消息方向的解决方案。我还没有找到一种优雅的方式来做到这一点。
提前感谢!
编辑:为了澄清"线程相互获取数据",这里有一个代码片段:
void MotorMessage::SetConnectedModules(MotorSensor &motor_sensor) {
out_buffer_[index_++] = motor_sensor.connected_.Get();
}
此处motor_sensor是对不同线程的引用,connected_是 SharedData 类型。
可以设置从传感器线程到使用者的一个或多个原子队列。这样,您就不必自己进行任何锁定。
例如,来自英特尔 TBB 的队列。
如果程序的设计方式是,在已经持有第一个锁定的互斥锁的同时,没有线程尝试锁定第二个互斥锁,那么将保证程序永远不会死锁。
如果您无法做到这一点,您仍然可以保证您的程序永远不会死锁,如果您确保每当任何线程尝试一次锁定多个互斥锁时,它都会尝试以与其他每个线程尝试锁定它们的顺序相同的顺序锁定这两个(或多个(互斥锁。 (即,只有当线程 #1 执行lock(mutex_a); lock(mutex_b);
而线程 #2 执行lock(mutex_b); lock(mutex_a);
时,您才有可能(因此最终(发生死锁(
至于如何做到这一点,如果你的程序足够小,重新设计它仍然很实用,那么一个好的设计是在线程之间传递消息而不是共享数据。 也就是说,如果线程 A 有一些线程 B 需要了解的数据,线程 A 应该将这些数据包装在某种消息/事件对象中,并将该对象发布到线程 B 将收到通知的消息/事件队列中,并在以后进行检查。 (由于 A 发布事件和 B 接收事件都不会阻塞超过一小段/有限的时间,因此这种方法避免了所有死锁的可能性( 请注意,即使要跨线程传递的数据量很大,如果通过shared_ptr
而不是创建数据副本传递数据,则此技术仍然有效。
如果OTOH你的程序已经太大/太复杂,无法重新设计,你的另一个选择是分析和调试你的程序死锁的原因,并提出必要的更改以避免这些死锁。 像valgrind的helgrind这样的工具可以帮助解决这个问题,通过在运行时自动检测不一致的互斥锁并告诉你它。 此外,如果可以使程序进入死锁状态,调试器可以显示每个线程被阻塞的位置,这可以引导您了解代码库中不一致的锁定顺序允许死锁发生的哪个位置。
除此之外,您始终可以使用包含线程 ID 和互斥锁唯一标识符(例如其内存位置(的printf()
来检测每个锁定/解锁命令,然后检查生成的日志输出以搜索输出中不一致的锁定模式。 如果手动执行此日志解析变得太困难,则可以使用工具自动执行此日志解析。