Локальные статические переменные для каждого потока

Допустим, у меня есть класс, который после инициализации создает поток и запускает в нем метод, внутри которого объявляется статическая переменная:

void method()
{
     static int var = 0;
     var++;
}

Если я создам больше объектов класса, например 3, то метод будет вызываться 3 раза в 3 разных потоках. После этого var будет равно 3. Как выполнить функциональность, где каждый поток имеет свой собственный статический var, который не зависит от других. Я был бы признателен всем за помощь.

Ответ 1

Вы можете использовать ключевое слово thread_local, которое указывает, что объект имеет длительность хранения потоков. Вы можете использовать его так:

static thread_local int V;

Если вам нужна дополнительная информация о спецификаторах класса хранения, вы можете проверить CppReference.

Ответ 2

Вот что спецификатор класса thread_local для

void method()
{
     thread_local int var = 0;
     var++;
}

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

Ответ 3

Вы сказали, в комментарии:

Мне нужна переменная, специфичная для каждого экземпляра класса

Именно то, что представляет собой переменная экземпляра (a.k.a. для каждого экземпляра).

static члены и функциональные локальные переменные не относятся к каждому экземпляру класса! Они либо полностью глобальны (один экземпляр на весь исполняемый файл), либо являются потоками, если вы используете С++ 11 и объявляете их thread_local.

Вам абсолютно нужна переменная-член. Это единственный способ гарантировать, что переменная будет специфичной для каждого экземпляра класса.

Вы можете утверждать, что вы создаете выделенный поток для каждого экземпляра класса. Прежде всего, скорее всего, вы не должны этого делать. Во-вторых, если вы когда-нибудь передумаете и перестанете создавать поток для каждого класса и, скажем, используйте пул потоков, ваш код мгновенно сломается.

Итак, правильная и простая вещь - иметь ее как переменную экземпляра (в отличие от переменной класса):

// OK - instance variable
class C { int var; };

// WRONG - class variable and lookalikes
class C { static int var; };
class C { void foo() { static int var; } };

// WRONG - thread variable, but **not** instance variable
class C { static thread_local int var; };
class C { void foo() { static thread_local int var; } };

Если вы хотите, вы можете указать свое намерение, указав имя метода в имени переменной:

class C {
  int foo_var;
  C() : foo_var(0) {}
  void foo() { ... }
};

Наконец, если вы в порядке с немного более типизированным, вы можете использовать оболочку-член для принудительного применения области, в которой она используется:

#include <utility>
#include <cassert>

template <typename T, typename Member, Member member>
class ScopedMember {
   T data;
public:
   explicit ScopedMember(const T & d) : data(d) {}
   explicit ScopedMember(T && d) : data(std::move(d)) {}
   ScopedMember() {}
   template <Member m, void(*)(char[member == m ? 1 : -1]) = (void(*)(char[1]))0>
   T & use() { return data; }
   template <Member m, void(*)(char[member == m ? 1 : -1]) = (void(*)(char[1]))0>
   const T & use() const { return data; }
};

class C {
public:
   C() : m_foo(-1) {}
   void granted() {
      auto & foo = m_foo.use<&C::granted>();
      foo = 5;
      assert(m_foo.use<&C::granted>() == 5);
   }
   void rejected() {
#if 0
      // Won't compile
      auto & foo = m_foo.use<&C::rejected>();
#endif
   }
private:
   ScopedMember<int, void(C::*)(), &C::granted> m_foo;
};

int main()
{
   C().granted();
   return 0;
}

Ответ 4

Ну, если вы хотите, чтобы переменная отличалась от потока к потоку, эта переменная не должна быть статичной. Он теряет точку статической переменной, которая по определению является переменной, которая:

  • Он разделяется всеми объектами этого класса.
  • Ему не нужен экземпляр класса (объект) для доступа.

Вопрос, который вы задаете ему, не "проблема с кодированием", а архитектурная. Я не знаю, какую систему/приложение вы разрабатываете, возможно, вам нужно подойти к вашей проблеме по-другому.

Задайте себе следующие вопросы:

  • Почему мне нужны потоки?
  • Зачем нужна эта переменная для статики?
  • Какую информацию мне нужно для обмена между потоками и какой информацией я не хочу делиться?

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