Как выделить локальное хранилище потоков?


У меня есть переменная в моей функции, которая является статической, но я хотел бы, чтобы она была статической на основе каждого потока.

Как я могу выделить память для моего класса C++ таким образом, что каждый поток имеет свою собственную копию экземпляра класса?

AnotherClass::threadSpecificAction()
{
  // How to allocate this with thread local storage?
  static MyClass *instance = new MyClass();

  instance->doSomething();
}

Это на Linux. Я не использую C++0x, а это gcc v3.4.6.

9 52

9 ответов:

#include <boost/thread/tss.hpp>
static boost::thread_specific_ptr< MyClass> instance;
if( ! instance.get() ) {
    // first time called by this thread
    // construct test element to be used in all subsequent calls from this thread
    instance.reset( new MyClass);
}
    instance->doSomething();

стоит отметить, что C++11 вводит thread_local ключевое слово.

вот пример спецификаторы длительности хранения:

#include <iostream>
#include <string>
#include <thread>
#include <mutex>

thread_local unsigned int rage = 1; 
std::mutex cout_mutex;

void increase_rage(const std::string& thread_name)
{
    ++rage;
    std::lock_guard<std::mutex> lock(cout_mutex);
    std::cout << "Rage counter for " << thread_name << ": " << rage << '\n';
}

int main()
{
    std::thread a(increase_rage, "a"), b(increase_rage, "b");
    increase_rage("main");

    a.join();
    b.join();

    return 0;
}

возможный выход:

Rage counter for a: 2
Rage counter for main: 2
Rage counter for b: 2

boost::thread_specific_ptr - это лучший способ как портативное решение.

в Linux & GCC вы можете использовать __thread модификатор.

Так что ваш экземпляр переменной будет выглядеть так:

static __thread MyClass *instance = new MyClass();

Если вы используете Pthreads вы можете сделать следующее:

//declare static data members
pthread_key_t AnotherClass::key_value;
pthread_once_t AnotherClass::key_init_once = PTHREAD_ONCE_INIT;

//declare static function
void AnotherClass::init_key()
{
    //while you can pass a NULL as the second argument, you 
    //should pass some valid destrutor function that can properly
    //delete a pointer for your MyClass
    pthread_key_create(&key_value, NULL);
}

void AnotherClass::threadSpecificAction()
{
  //Initialize the key value
  pthread_once(&key_init_once, init_key);

  //this is where the thread-specific pointer is obtained
  //if storage has already been allocated, it won't return NULL

  MyClass *instance = NULL;
  if ((instance = (MyClass*)pthread_getspecific(key_value)) == NULL)
  {
    instance = new MyClass;
    pthread_setspecific(key_value, (void*)instance);
  }

  instance->doSomething();
}

C++11 указывает a thread_local тип хранения, просто использовать его.

AnotherClass::threadSpecificAction()
{
  thread_local MyClass *instance = new MyClass();
  instance->doSomething();
}

одна дополнительная оптимизация заключается в том, чтобы также выделять на потоке локальное хранилище.

Если вы работаете с MSVC++, вы можете прочитать локальное хранилище потока (TLS)

и тогда вы можете увидеть это пример.

кроме того, будьте в курсе правила и ограничения для TLS

в Windows вы можете использовать TlsAlloc и TlsFree чтобы выделить хранилище в локальном хранилище потоков.

чтобы установить и получить значения в TLS, вы можете использовать TlsSetValue и TlsGetValue, соответственно

здесь вы можете увидеть пример того, как он будет использоваться.

просто замечание... MSVC++ поддерживает declspec (поток) от VSC++2005

#if (_MSC_VER >= 1400)
  #ifndef thread_local     
    #define thread_local __declspec(thread)
  #endif
#endif

основная проблема (которая решается в boost:: thread_specific_ptr) переменные, помеченные им, не могут содержать ctor или dtor.

Folly (Facebook Open-source Library) имеет портативную реализацию локального хранилища потоков.

По мнению его авторов:

улучшено локальное хранилище потоков для нетривиальных типов (аналогичная скорость, как pthread_getspecific но потребляет только один pthread_key_t, и в 4 раза быстрее чем boost::thread_specific_ptr).

Если вы ищете портативную реализацию локального потока хранения, эта библиотека является хорошим вариантом.