我使用一个简单的模板类来提供线程本地存储。这只是包装了一个std::map
和一个关键部分。这不会受到任何平台特定线程本地问题的影响,唯一的平台要求是获取当前线程 ID 为整数。它可能比本地线程本地存储慢一点,但它可以存储任何数据类型。
下面是我的代码的精简版。我删除了默认值逻辑以简化代码。T
由于它可以存储任何数据类型,因此增量和减量运算符仅在支持它们时才可用。关键部分只需要保护查找和插入到地图中。一旦返回一个引用,使用 unprotected 是安全的,因为只有当前线程会使用这个值。
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
can useThreadLocal::AutoCleanup
来清理变量。
或者在 DBConnection 的情况下
~DBConnection() {
if (--m_connectionCount == 0)
ThreadLocal<int>::cleanup(m_connectionCount);
}
该cleanup()
方法是静态的,以免干扰operator T()
。可以使用全局函数调用 this 来推断模板参数。