2012-07-19 42 views
1

我在几个场所我去过,但没有一个满意的解决方案已经看到了一个设计问题:含和访问线程的全局数据的C++

假设你有一个线程的动态数量的系统。

每个线程都必须有一组“单身”,单身人士有一个和每个线程(因此它们不是真正的单身人士,但每个线程单身)

这套单身的只有一个实例的访问在编译时已知。

每个单身的有一个默认的构造函数(为了简化的东西,不过,这并没有这个约束的解决方案将是巨大的)

满意的解决方案应该具备如下:

  1. 每个线程可以访问的任何其单身的在O(1)时间

  2. 到一个单接入是锁定自由

  3. 添加一个单独的“单套”不需要写在一组方新代码

  4. 的“单套”在编译时

填充我不知道如果这样的一个设计是可行的。如果是这样,我认为它需要一点元编程。

预先感谢您的任何见解。

+0

这是不可能的模糊和不明确的 - 甚至不清楚问题是什么。 – 2012-07-19 08:12:15

回答

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了解更多详情。

+0

除非您遇到了2011年之前的编译器,否则最好使用标准的'thread_local'关键字,而不是'__thread'这样的特定于编译器的扩展。 – 2012-07-19 08:57:59

+0

你说得对,那会更好。但即使是最好的2012年编译器也不支持它。 http://gcc.gnu.org/gcc-4.7/cxx0x_status.html – 2012-07-19 09:05:17

0

好,通常我会在评论中发布此,因为我不是很肯定,如果我甚至正确理解你的问题......不过,是不是足以创造你单身在构造函数中设置的情况下,父Thread类?

假设你有三个类A,B和C(在编译时已知) 和一个“线程”类。

你会不会在你的线程头

class Thread { 
private: 
    A *a; 
    B *b; 
    C *c; 
public: 
    Thread(); 
} 

声明A,B和C的实例,然后在你的线程的构造函数实例化呢?

Thread:Thread() { 
    a = new A(); 
    b = new B(); 
    c = new C(); 
} 

这样,每个线程“拥有”的单身人士完全,这意味着它可以在任何时间访问它们,而不必担心竞争条件或锁。

关于“添加单例”,我可以建议创建一个'Singleton-Parent'类,然后使用标准容器(如std :: list),您可以将新指针推向? 当然,访问这个列表时必须用锁来保护,当你在运行时,在编译时,这是不必要的。 在编译期间,您可能最好使用静态数组指针,这将允许您尽可能快地访问指针。

同样,如果我理解你的问题错了抱歉。

+0

谢谢,您提出的解决方案是我提供的最佳解决方案。我的问题是,无论何时我想添加一个新类,我都必须编译线程以及任何正在使用它的人。我希望'线程'本来是一个可以重用的服务或库。 – Ezra 2012-07-19 08:20:41

+0

@Ezra不,其实不是。你看,当向Thread添加一个新类时,你重新编译了Thread ...并完成了。所有'使用'线程的人都不会受到影响,只要你不改变'线程'的头部就可以。 (这意味着你必须使用std:list而不是静态数组,或者只需在头文件中放置一个指针* Singleton,然后在Thread构造函数中用'new Singleton [x]动态创建数组'; 请不要忘记释放你以这种方式实例化的类 – ATaylor 2012-07-19 08:30:57

+0

它太过侵入性和僵化性'线程'必须能够访问'A','B'和'C'的构造函数,并且创建那些可能需要构造函数的参数,直到后来可能不可用 – 2012-07-19 08:34:35

-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标准库中的功能。

+1

互斥体缺失,代码不是线程安全的。 – 2012-07-19 08:35:39

+0

@MaximYegorushkin我不打算完成这个,只是举个简单的例子。 – 2012-07-19 08:40:44

1

除非我误解了你,否则你正在描述线程本地存储。

在C++ 11中,您只需声明变量thread_local即可为每个线程获取单独的实例。

在C++ 03中,最便携的解决方案是boost::thread_specific_ptr;或者,您的编译器和/或系统库可能会提供线程特定的存储,如POSIX的pthread_key_create和朋友。

+0

我会推荐使用预标准的'__thread'存储声明器([大多数编译器都支持它很长一段时间](http://en.wikipedia.org/wiki/Thread-local_storage#Language-specific_implementation)),敏感的代码。 'boost :: thread_specific_ptr'虽然是一个可移植的解决方案需要库调用来访问存储的指针。 – 2012-07-19 12:19:36