我在过去的几个工作场所看到的一个设计问题,但没有一个令人满意的解决方案:
假设您的系统具有动态线程数。
每个帖子必须能够访问一组“单身人士”,单身人士每个帖子只有一个实例(因此他们不是真正的单身人士,而是每个线程的单身人士)
这组单例在编译时是已知的。
每个单身人士都有一个默认的构造函数(为了简化事情,但是,没有这种约束的解决方案会很棒)
满意的解决方案应具备以下条件:
每个帖子都可以在o(1)时间访问其任何单身人士
访问单身人士是免费的
将单例添加到'singleton set'不需要在set侧写入新代码
在编译期间填充'singleton set'
我不确定这样的设计是否可行。如果是,我认为它需要一点元编程。
提前感谢您的任何见解。
答案 0 :(得分:1)
线程局部变量可以很好地解决问题。
// in .h
class ThreadSingleton
{
private:
static __thread ThreadSingleton* thread_specific_instance;
public:
static ThreadSingleton* get() { return thread_specific_instance; }
ThreadSingleton();
~ThreadSingleton();
};
// in .cc
__thread ThreadSingleton* ThreadSingleton::thread_specific_instance;
ThreadSingleton::ThreadSingleton() {
if(thread_specific_instance)
std::abort(); // one instance per thread please
thread_specific_instance = this;
}
ThreadSingleton::~ThreadSingleton() {
thread_specific_instance = 0;
}
// usage
int main() {
// on thread entry
ThreadSingleton x;
// later anywhere in the thread
ThreadSingleton* px = ThreadSingleton::get();
}
每个线程在堆栈的某处创建ThreadSingleton
,通常在线程函数中。稍后ThreadSingleton
可以通过ThreadSingleton::get()
从该线程中的任何位置访问,它返回调用线程的单例。 (以上可以作为一个模板来包装任何其他类,我没有为简单的阐述而这样做。)
以性能方式访问线程局部变量不需要任何调用(与使用pthread_key_create
创建的特定于线程的存储不同)。有关详细信息,请参阅http://www.akkadia.org/drepper/tls.pdf。
答案 1 :(得分:1)
除非我误解你,否则你正在描述线程本地存储。
在C ++ 11中,您只需要声明一个变量thread_local
来为每个线程获取一个单独的实例。
在C ++ 03中,最便携的解决方案是boost::thread_specific_ptr
;或者,您的编译器和/或系统库可能提供特定于线程的存储,例如POSIX的pthread_key_create
和朋友。
答案 2 :(得分:0)
好吧,通常我会在评论中发布这个,因为我不太确定我是否正确地理解了你的问题...但是不足以在构造函数中创建你的单例集的实例。 parent Thread class?
假设您有三个类A,B和C(在编译时已知) 还有一个'Thread'课程。
您不会在线程头
中声明A,B和C的实例class Thread {
private:
A *a;
B *b;
C *c;
public:
Thread();
}
然后在你的线程的构造函数中实例化它们?
Thread:Thread() {
a = new A();
b = new B();
c = new C();
}
这样,每个线程都“独占”了单身人士,这意味着它可以随时访问它们,而不必担心竞争条件或锁定。
关于“添加单例”,我是否可以建议创建一个“Singleton-Parent”类,然后使用标准容器(如std :: list)将新指针推送到该容器上? 当然,访问此列表必须受到锁的保护,当您在运行时在编译时进行此操作时,这不是必需的。 在编译期间,您最好使用静态指针数组,这样您就可以尽快访问指针。
再次,抱歉,如果我理解你的问题是错误的。
答案 3 :(得分:-1)
我不确定我是否理解你的问题,但对我来说,单身的“集合”是无关紧要的。您有一定数量的单身人士,称他们为Singleton1
至SingletonX
,其中X
在编译时已知。这对线程来说并不重要。
对于实际的单例,您可以让它们从处理每线程部分的单个模板化基类继承。像这样:
template<class B>
struct SingletonBase
{
static B &getInstance()
{
// One instance per thread
static std::unordered_map<std::thread::id, B> intances;
if (instances.find(std::this_thread::get_id()) == instances.end())
instances[std::this_thread::get_id()] = B{};
return instances[std::this_thread::get_id()];
}
};
class Singleton1 : public SingletonBase<Singleton1>
{
// ...
};
如果您不想为单身人士设置单独的不同类,可以使用std::array
来存储它们:
class Singleton : public SingletonBase<Singleton>
{
// ...
};
std::array<Singleton, X> singletons;
这将在编译时创建指定数字X
的数组,并且可以像普通数组一样访问:Singleton &instance = singletons[0].getInstance();
。
请注意,我的示例代码使用了“新”C ++ 11标准库中的功能。