包含和访问c ++中的线程全局变量

时间:2012-07-19 07:56:53

标签: c++ oop metaprogramming

我在过去的几个工作场所看到的一个设计问题,但没有一个令人满意的解决方案:

假设您的系统具有动态线程数。

每个帖子必须能够访问一组“单身人士”,单身人士每个帖子只有一个实例(因此他们不是真正的单身人士,而是每个线程的单身人士)

这组单例在编译时是已知的。

每个单身人士都有一个默认的构造函数(为了简化事情,但是,没有这种约束的解决方案会很棒)

满意的解决方案应具备以下条件:

  1. 每个帖子都可以在o(1)时间访问其任何单身人士

  2. 访问单身人士是免费的

  3. 将单例添加到'singleton set'不需要在set侧写入新代码

  4. 在编译期间填充'singleton set'

  5. 我不确定这样的设计是否可行。如果是,我认为它需要一点元编程。

    提前感谢您的任何见解。

4 个答案:

答案 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)

我不确定我是否理解你的问题,但对我来说,单身的“集合”是无关紧要的。您有一定数量的单身人士,称他们为Singleton1SingletonX,其中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标准库中的功能。