2012-07-19 25 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