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