The problem itself:
class B{/*...*/};
class A {
/* members */
NON-static thread_local B var; // and a thread_local variable here
If you're willing to use tbb (which is free even though by Intel), you could use their tbb::enumerable_thread_specific<T> template class (which essentially is something like std::unordered_map<thread_id,T>
but lock free, I understand). Since the A
are shared between threads, one such container per instance of A
is required, but it appears B
is better declared as a nested type. For example
class A
{
struct B
{
B(const A*);
void call(/* args */);
};
tbb::enumerable_thread_specific<B> _B ([&]()->B { return {this}; } );
void method(/* args */)
{
_B.local().call(/* args */); // lazily creates thread local B if required.
}
/* ... */
};
Where available, you could use pthread
-functions pthread_getspecific
and pthread_setspecific
for a getter and a setter for that purpose:
#include <pthread.h>
class A {
private:
#define varKey 100L
public:
int getVar() {
void *mem = pthread_getspecific(varKey);
if(mem)
return *((int*)mem);
else
return 0;
}
void setVar(int val) {
void *mem = malloc(sizeof(int));
*((int*)mem)=val;
pthread_setspecific(varKey, mem);
}
~A() {
void *mem = pthread_getspecific(varKey);
if (mem)
free(mem);
}
};
You can't have a non-static member declared thread_local
. See cppreference. In particular:
the
thread_local
keyword is only allowed for objects declared at namespace scope, objects declared at block scope, and static data members.
If you don't want to use pthreads (tricky on Windows), some container is your only option.
One choice is a variant of std::unordered_map<THREAD_IDENTIFIER_TYPE, B>
. (You could write a class to wrap it and protect the map with a mutex.)
Another initially attractive option is a thread_local
static member of A
which maps A*
to B
will avoid any need for locks.
class A {
static thread_local std::unordered_map<A*, B> s_B;
....
};
usage:
void A::foo() {
B& b = s_B[this]; // B needs to be default constructable.
...
The catch is that you need some way to remove elements from the s_B
map. That's not too much of a problem if A
objects are actually locked to a particular thread, or if you have some way to invoke functions on another thread - but it's not entirely trivial either. (You may find it safer to use a unique identifier for A
which is an incrementing 64-bit counter - that way there is much less risk of the identifier being reused between destroying the A
object and the message to remove the B
from all the maps being processed.)