这个问题是关于在工作线程中使用单例对象的getter方法。首先是一些伪代码:
// Singleton class which contains data
class MyData
{
static MyData* sMyData ;
int mData1[1024];
int mData2[1024];
int mData3[1024];
MyData* getInstance()
{
// sMyData is created in the very beginning.
return sMyData ;
}
void getValues(int idx, int& data1,int& data2,int& data3)
{
data1 = mData1[idx];
data2 = mData2[idx];
data3 = mData3[idx];
}
int* getData1()
{
return &mData1[0];
}
}
class MyThread
{
void workerMethod()
{
MyData* md = MyData::getInstance();
int d1,d2,d3;
md->getValue( 12, d1,d2,d3 );
int* data1 = md->getData1();
d1 = data1[34];
}
}
现在你看到我有一些getter方法(所有只读),MyData :: getInstance(),MyData :: getValue()和MyData :: getData1()。第一个问题是这些方法的线程安全性如何?
由于它们通常被称为方法,因此我应该避免使用互斥锁来保护这些方法。
第二个问题是:在多线程应用程序中从中央源读取数据的建议方法是什么,尤其是在工作方法中。
谢谢!
保
答案 0 :(得分:5)
如果没有其他线程会尝试写入单例对象中的数据,则不需要保护它们:根据定义,在没有编写器的情况下,多个读取器是线程安全的。这是一个常见的模式,程序的初始化代码设置一个单例,然后只能由工作线程读取。
但是,如果任何线程曾写入此数据而其他人正在从中读取数据,则必须以某种方式保护它。如果你有很多读者,只有偶尔的作家,那么值得考虑某种“读写”锁定,它允许多个读者在没有任何作者的情况下。
答案 1 :(得分:2)
无法判断这是否是线程安全的。如果数据在对象创建期间初始化,并且永远不会更改,则此操作将正确运行。如果你通过其他方法改变底层数据,那么读者将不得不对作者进行某种同步,没有办法解决这个问题。
根据您正在做的事情,您可能能够使用比互斥量更轻的东西,例如原子更新同步命令,或使用读写器锁,但不知道您正在做什么,这是不可能的告诉。
答案 2 :(得分:1)
你的方法没问题,但你有两个主要问题。
首先,MyData :: getInstance()应该是静态的,不会创建实例。为了最大限度地减少互斥锁的使用,您应该查看double checked lock。
其次,线程安全取决于getter方法的调用者而不是MyData类。如果这就是你想要的,那就太好了。如果没有,那么你将不得不在MyClass中提出某种访问控制。此外,由于您包含的数据类型只是一个基本类型(int),因此在您的代码投入生产之前,您将永远不会看到任何同步问题。
答案 3 :(得分:1)
首先回过头来阅读这个问题以获得更好的singelton版本:
Can any one provide me a sample of Singleton in c++?
另请注意: NOT 使用单身作为美化的全球变量 它只会增加糟糕设计的复杂性。只需使用全局变量。
只要你只是从单身人士那里读书,它在使用时就是线程安全的。
唯一不是线程安全的点(语言无法保证)是在创建期间。所以从技术上讲,你应该在创建实例的部分周围添加锁,以保证在任何人都可以使用它之前完全创建了单例。
注意:不要使用双重检查锁定来诱使锁定策略优化。它可以 NOT 在C ++中正常工作。阅读此DDJ article。
如果你能保证在单线程环境中(在创建任何线程之前)实例化单例(可能是第一次调用getInstance()),那么你可以在实例化期间免除锁的需要。
如果您更改代码以便其他线程可以写入单例,那么您需要考虑锁定和一致性。如果您的写入不是原子的,则只需要锁定。如果你的写只是更新一个整数,它已经是原子的,不需要改变。如果您的代码不是原子的:
然后,您需要锁定对象,直到所有写入完成,因此对具有读取权限的方法具有锁定,以阻止它们从对象读取,而另一种方法更新对象。
因此,这是成员变量只能通过成员方法访问的另一个原因。
答案 4 :(得分:0)
对于线程安全,您需要查看整个类。如上所述,您的类将不是线程安全的。虽然你的getValues方法没问题,但是getData1方法存在问题。
你说他们是(只读)getter方法。但是,两者都没有声明为const方法。 getData1作为const方法无效,因为它返回非const指针。此外,当您公开实现时,返回指向私有类数据的指针是不好的。
如果这是一个单例类,在线程开始之前在初始化时保存一些基本上静态的数据集,那么所有的访问器都应该是const方法。 getInstance还应该返回一个指向该类的const指针(并且是另一个答案所提到的静态方法)。