単純なテンプレートクラスを使用して、スレッドローカルストレージを提供します。これは単にstd::map
とクリティカルセクションをラップします。これにより、プラットフォーム固有のスレッドローカルの問題が発生することはありません。唯一のプラットフォーム要件は、現在のスレッドIDを整数で取得することです。ネイティブスレッドローカルストレージよりも少し遅いかもしれませんが、任意のデータ型を保存できます。
以下は私のコードの縮小版です。コードを簡略化するために、デフォルト値のロジックを削除しました。任意のデータ型を格納できるため、インクリメント演算子とデクリメント演算子は、T
それらをサポートしている場合にのみ使用できます。クリティカルセクションは、マップの検索と挿入を保護するためにのみ必要です。参照が返されると、現在のスレッドのみがこの値を使用するため、保護されていない状態で安全に使用できます。
template <class T>
class ThreadLocal
{
public:
operator T()
{
return value();
}
T & operator++()
{
return ++value();
}
T operator++(int)
{
return value()++;
}
T & operator--()
{
return --value();
}
T operator--(int)
{
return value()--;
}
T & operator=(const T& v)
{
return (value() = v);
}
private:
T & value()
{
LockGuard<CriticalSection> lock(m_cs);
return m_threadMap[Thread::getThreadID()];
}
CriticalSection m_cs;
std::map<int, T> m_threadMap;
};
このクラスを使用するために、私は通常、クラス内で静的メンバーを宣言します。
class DBConnection {
DBConnection() {
++m_connectionCount;
}
~DBConnection() {
--m_connectionCount;
}
// ...
static ThreadLocal<unsigned int> m_connectionCount;
};
ThreadLocal<unsigned int> DBConnection::m_connectionCount
すべての状況に最適というわけではありませんが、私のニーズをカバーしており、不足している機能を見つけたら簡単に追加できます。
bdonlanは正しいです。この例では、スレッドが終了した後はクリーンアップされません。ただし、これは手動でクリーンアップを追加するのは非常に簡単です。
template <class T>
class ThreadLocal
{
public:
static void cleanup(ThreadLocal<T> & tl)
{
LockGuard<CriticalSection> lock(m_cs);
tl.m_threadMap.erase(Thread::getThreadID());
}
class AutoCleanup {
public:
AutoCleanup(ThreadLocal<T> & tl) : m_tl(tl) {}
~AutoCleanup() {
cleanup(m_tl);
}
private:
ThreadLocal<T> m_tl
}
// ...
}
次に、それが変数をクリーンアップするためにそのメイン関数でThreadLocal
使用できるを明示的に使用することを知っているスレッド。ThreadLocal::AutoCleanup
またはDBConnectionの場合
~DBConnection() {
if (--m_connectionCount == 0)
ThreadLocal<int>::cleanup(m_connectionCount);
}
メソッドは、cleanup()
干渉しないように静的operator T()
です。グローバル関数を使用してこれを呼び出すことができ、テンプレートパラメータを推測します。