我不确定这是关于编程技巧或设计的问题,但我愿意接受建议。
问题:我想在数据源(传感器)和消费者之间创建一个抽象层。这个想法是消费者只“知道”不同传感器类型的接口(抽象基类)。每种传感器类型通常由几个单独的值组成,这些值都有自己的吸气方法。
作为一个例子,我将使用简化的GPS传感器。
class IGpsSensor {
public:
virtual float getLongitude() = 0;
virtual float getLatitude() = 0;
virtual float getElevation() = 0;
// Deviations
virtual float getLongitudeDev() = 0;
virtual float getLatitudeDev() = 0;
virtual float getElevationDev() = 0;
virtual int getNumOfSatellites() = 0;
};
由于传感器的更新是由不同的线程完成的(细节取决于接口的实现),因此同步getter以及更新方法似乎是确保一致性的合理方法。
到目前为止一切顺利。在大多数情况下,这种级别的同步应该足够了。但是,有时可能需要获取多个值(使用连续的getXXX()调用)并确保两者之间不发生更新。这是否必要(以及哪些值很重要)取决于消费者。
坚持这个例子,在很多情况下,了解经度和纬度(但希望两者都与相同的更新()相关)非常重要。我承认可以将它们组合成一个“位置”类或结构。但消费者也可能将传感器用于更复杂的算法,并且也需要偏差。
现在我想知道,做这件事的正确方法是什么。
我能想到的解决方案:
将所有可能的值分组到一个struct(或类)中,并添加一个额外的(synchronized)getter一次性返回所有值的副本 - 对于我来说似乎是很多不必要的开销,如果只有2或3个可能需要10个值。
添加一个方法,返回对数据源中使用的互斥锁的引用,以允许消费者锁定 - 这不像“好设计”。由于getter已经同步,因此必须使用递归互斥锁。但是,我假设有多个读者,但只有一个作者,因此我宁愿在这里使用共享互斥锁。
感谢您的帮助。
答案 0 :(得分:2)
如何暴露“读者”界面?要获得读者对象,您可以这样做:
const IGpsSensorReader& gps_reader = gps_sensor.getReader();
IGpsSensorReader类可以访问IGpsSensor类的受保护成员。构建时,它将获得锁定。破坏后,它会释放锁定。访问者可以这样做:
{ //block that accesses attributes
const IGpsSensorReader& gps_reader = gps_sensor.getReader();
//read whatever values from gps_reader it needs
} //closing the scope will destruct gps_reader, causing an unlock
您还可以将getWriter方法公开给执行更新的线程。在内部,您可以使用boost的shared_mutex来调解读者和作者之间的访问。
答案 1 :(得分:2)
我在一些简单项目中使用的技术是仅提供对代理对象的访问。此代理对象在其生存期内持有锁,并为我的数据提供实际接口。此访问本身不进行同步,因为它只能通过已经适当锁定的代理才能使用。我从未试图将其扩展到一个完整的项目,但它似乎对我的目的很好。
答案 2 :(得分:1)
可能的解决方案:从
派生所有源类class Transaction {
pthread_mutex_t mtx;
// constructor/destructor
public:
void beginTransaction() { pthread_mutex_lock(&mtx); } // ERROR CHECKING MISSING
void endTransaction() { pthread_mutex_unlock(&mtx); } // DO ERROR CHECKING
protected:
// helper method
int getSingle(int *ptr)
{ int v; beginTransaction(); v=*ptr; endTransaction(); return v; }
};
如果需要读出多个值,请使用begin / endTransaction方法。要定义你的getValue函数,只需用指向相应成员的指针调用getSingle [这只是一个方便的方法,这样你就不必在每个getValue函数中调用begin / endTransaction。]。
您需要充实一些细节,因为如果您的getValue函数使用begin / endTransaction,您将无法在事务中调用它们。 (互斥锁只能被锁定一次,除非它被配置为递归。)