Я второй комментарии Разработчика SQL. FWIW, более новые версии Жабы намного более стабильны, чем золотые старые произведения были. Я использую Жабу 9.7, и это довольно хорошо.
В Linux, если вы используете __ thread
данные TLS, единственное ограничение устанавливается вашим доступным адресным пространством. , поскольку эти данные просто выделяются как обычная оперативная память, на которую ссылаются дескрипторы сегментов gs
(на x86) или fs
(на x86-64). Обратите внимание, что в некоторых случаях выделение данных TLS, используемых динамически загружаемыми библиотеками, может быть исключено в потоках, которые не используют эти данные TLS.
TLS выделен pthread_key_create
и другими , однако, ограничен слотами PTHREAD_KEYS_MAX
(это относится ко всем соответствующим реализациям pthreads).
Для получения дополнительной информации о реализации TLS в Linux,
Может случиться так, что в документации по ускорению просто говорится об общем настраиваемом лимите, а не обязательно о жестком ограничении платформы. В Linux команда ulimit ограничивает ресурсы, которые могут иметь процессы (количество потоков, размер стека, память и множество других вещей). Это косвенно повлияет на локальное хранилище вашего потока. В моей системе, похоже, нет записи в ulimit, специфичной для локального хранилища потоков. У других платформ может быть способ указать это самостоятельно. Кроме того, я думаю, что во многих многопроцессорных системах локальное хранилище потока будет в памяти, выделенной для этого процессора, поэтому вы можете столкнуться с ограничениями физической памяти задолго до того, как вся память в системе будет исчерпана. Я бы предположил, что в этой ситуации есть какое-то резервное поведение для поиска данных в основной памяти, но я не знаю. Как видите, я о многом гадаю. Надеюсь, он по-прежнему ведет вас в правильном направлении ...
Я использовал TLS только в Windows, и между версиями есть небольшие различия в том, сколько можно использовать: http://msdn.microsoft.com/en-us/library/ms686749 (VS.85) .aspx
Я предполагаю, что ваш код нацелен только на операционные системы, поддерживающие потоки - в прошлом я работал со встроенными и настольными ОС, которые не поддерживают потоки, поэтому не поддерживают TLS.
Declspec локального хранилища потока в Windows ограничивает вас использованием его только для статических переменных, что означает, что вам не повезло, если вы хотите использовать его более творческими способами.
Там - это низкоуровневый API в Windows, но у него нарушена семантика, из-за чего его очень неудобно инициализировать: вы не можете сказать, была ли переменная уже видна вашим потоком, поэтому вам нужно явно инициализировать ее при создании
С другой стороны, pthread API для локального хранилища потока хорошо продуман и гибок.
Я использую простой шаблонный класс для обеспечения локального хранилища потоков. Это просто обертывает std :: map
и критический раздел. Тогда это не страдает от каких-либо локальных проблем потока, специфичных для платформы, единственное требование платформы - получить текущий идентификатор потока, как в целочисленном. Это может быть немного медленнее, чем локальное хранилище собственных потоков, но оно может хранить данные любого типа.
Ниже представлена урезанная версия моего кода. Я удалил логику значений по умолчанию, чтобы упростить код. Поскольку он может хранить данные любого типа, операторы увеличения и уменьшения доступны только в том случае, если 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 ()
является статическим, чтобы не мешать оператору T ()
. Для его вызова можно использовать глобальную функцию, которая выводит параметры шаблона.
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 ()
является статическим, чтобы не мешать оператору T ()
. Для его вызова можно использовать глобальную функцию, которая выведет параметры шаблона.
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 ()
является статическим, чтобы не мешать оператору T ()
. Для его вызова можно использовать глобальную функцию, которая выведет параметры шаблона.
На Mac я знаю о Хранилище для конкретных задач в API Многопроцессорных служб API:
MPAllocateTaskStorageIndex
MPDeallocateTaskStorageIndex
MPGetTaskStorageValue
MPSetTaskStorageValue
Это очень похоже на локальное хранилище потоков Windows.
Я не уверен, рекомендуется ли этот API в настоящее время для локального хранилища потоков на Mac. Возможно есть что-то новее.