在 C++ 中将所有 TLS(线程本地存储)变量设置为新的单个值
Setting all TLS (thread local storage) variables to a new, single value in C++
我有一个 class Foo 具有以下线程特定的静态成员:
__declspec(thread) static bool s_IsAllAboutThatBass;
在实现文件中是这样初始化的:
__declspec(thread) bool Foo::s_IsAllAboutThatBass = true;
到目前为止一切顺利。现在,任何线程都可以随意翻转此 bool
,因为他们认为合适。然后是问题:在某些时候我希望每个线程将 bool
重置为其初始 true
值。
如何从中央线程将 TLS 的所有实例设置为 true?
我已经想过用我知道的同步原语(如关键部分、read/write 部分或事件)来执行此操作的方法,但没有一个符合我的要求。在我的实际用例中 我无法在任何很长一段时间内阻塞任何其他线程。
感谢任何帮助。谢谢!
编辑:计划 A
一个想法是使用一个生成令牌,或cookie,它被所有线程读取并被中央线程写入。然后,每个线程在通过某个访问器获取 s_isAllAboutThatBass
时,都可以拥有该线程查看的最后一代的 TLS。当线程本地 cookie 与共享 cookie 不同时,我们增加线程本地 cookie 并将 s_isAllAboutThatBass
更新为 true
。
最简单的答案是:不能。之所以称为线程本地存储,是因为只有它的线程才能访问它。根据定义,这意味着其他 "central thread" 无法访问它。根据定义,这就是全部。
现在,根据您的硬件和编译器平台实现 TLS 的方式,如果您的 TLS 实现通过将 TLS 变量映射到不同的虚拟内存地址来工作,则可能会有一个技巧。通常情况下,一个 CPU 寄存器是特定于线程的,它被设置为指向不同的内存地址,并且所有 TLS 变量都作为相对地址进行访问。
如果是这种情况,您或许可以派生出一些线程安全机制,每个线程都可以通过该机制获取指向其 TLS 变量的指针,并将其放入非 TLS 容器中,您的 "central thread" 可以到。
当然,您必须使所有这些与您的线程保持同步,并在每个线程终止后进行清理。
您必须通过一个简单的测试来确定在您的平台上是否属于这种情况:声明一个 TLS 变量,然后比较它在两个不同线程中的指针地址。如果不同,您可以通过这种方式解决它。从技术上讲,这种指针比较是不可移植的,并且实现已定义,但此时您已经深入了解了特定于实现的行为。
但如果地址相同,则表示您的实现使用虚拟内存寻址来实现TLS。只有执行线程可以访问其 TLS 变量 period,并且任何 "central thread" 都无法查看其他线程的 TLS 变量。它由您的操作系统内核强制执行。 "central thread" 必须与每个线程合作,并使用典型的线程间通信方式安排访问线程的 TLS 变量。
cookie 方法可以很好地工作,并且您不需要使用 TLS 槽来实现它,只需在您的线程过程中使用一个局部变量即可。要处理 cookie 在线程创建时间和线程启动时间之间更改值的情况 运行(有一个小延迟),您必须将当前 cookie 值作为输入参数传递给线程创建,然后您的线程过程可以在开始检查活动 cookie 的更改之前将其局部变量初始化为该值。
intptr_t g_cookie = 1;
pthread_rwlock_t g_lock;
void* thread_proc(void *arg)
{
intptr_t cookie = (intptr_t)arg;
while (keepRunningUntilSomeCondition)
{
pthread_rwlock_rdlock(&g_lock);
if (cookie != g_cookie)
{
cookie = g_cookie;
s_IsAllAboutThatBass = true;
}
pthread_rwlock_unlock(&g_lock);
//...
}
pthread_exit(NULL);
}
void createThread()
{
...
pthread_t thread;
pthread_create(&thread, NULL, &thread_proc, (void*)g_cookie);
...
}
void signalThreads()
{
pthread_rwlock_wrlock(&g_lock);
++g_cookie;
pthread_rwlock_unlock(&g_lock);
}
int main()
{
pthread_rwlock_init(&g_lock, NULL);
// use createThread() and signalThreads() as needed...
pthread_rwlock_destroy(&g_lock);
return 0;
}
这是 "Plan A" 使用 C++11 标准 atomic
variable and thread_local
-specifier 的轻量级实现。 (如果您的编译器不支持它们,请替换为供应商特定设施。)
#include <atomic>
struct Foo {
static std::atomic<unsigned> s_TokenGeneration;
static thread_local unsigned s_LocalToken;
static thread_local bool s_LocalState;
// for central thread
void signalResetIsAllAboutThatBass() {
++s_TokenGeneration;
}
// accessor for other threads
void setIsAllAboutThatBass(bool b) {
unsigned currToken = s_TokenGeneration;
s_LocalToken = currToken;
s_LocalState = b;
}
bool getIsAllAboutThatBass() const {
unsigned currToken = s_TokenGeneration;
if (s_LocalToken < currToken) {
// reset thread-local token & state
s_LocalToken = currToken;
s_LocalState = true;
}
return s_LocalState;
}
};
std::atomic<unsigned> Foo::s_TokenGeneration;
thread_local unsigned Foo::s_LocalToken = 0u;
thread_local bool Foo::s_LocalState = true;
我有一个 class Foo 具有以下线程特定的静态成员:
__declspec(thread) static bool s_IsAllAboutThatBass;
在实现文件中是这样初始化的:
__declspec(thread) bool Foo::s_IsAllAboutThatBass = true;
到目前为止一切顺利。现在,任何线程都可以随意翻转此 bool
,因为他们认为合适。然后是问题:在某些时候我希望每个线程将 bool
重置为其初始 true
值。
如何从中央线程将 TLS 的所有实例设置为 true?
我已经想过用我知道的同步原语(如关键部分、read/write 部分或事件)来执行此操作的方法,但没有一个符合我的要求。在我的实际用例中 我无法在任何很长一段时间内阻塞任何其他线程。
感谢任何帮助。谢谢!
编辑:计划 A
一个想法是使用一个生成令牌,或cookie,它被所有线程读取并被中央线程写入。然后,每个线程在通过某个访问器获取 s_isAllAboutThatBass
时,都可以拥有该线程查看的最后一代的 TLS。当线程本地 cookie 与共享 cookie 不同时,我们增加线程本地 cookie 并将 s_isAllAboutThatBass
更新为 true
。
最简单的答案是:不能。之所以称为线程本地存储,是因为只有它的线程才能访问它。根据定义,这意味着其他 "central thread" 无法访问它。根据定义,这就是全部。
现在,根据您的硬件和编译器平台实现 TLS 的方式,如果您的 TLS 实现通过将 TLS 变量映射到不同的虚拟内存地址来工作,则可能会有一个技巧。通常情况下,一个 CPU 寄存器是特定于线程的,它被设置为指向不同的内存地址,并且所有 TLS 变量都作为相对地址进行访问。
如果是这种情况,您或许可以派生出一些线程安全机制,每个线程都可以通过该机制获取指向其 TLS 变量的指针,并将其放入非 TLS 容器中,您的 "central thread" 可以到。
当然,您必须使所有这些与您的线程保持同步,并在每个线程终止后进行清理。
您必须通过一个简单的测试来确定在您的平台上是否属于这种情况:声明一个 TLS 变量,然后比较它在两个不同线程中的指针地址。如果不同,您可以通过这种方式解决它。从技术上讲,这种指针比较是不可移植的,并且实现已定义,但此时您已经深入了解了特定于实现的行为。
但如果地址相同,则表示您的实现使用虚拟内存寻址来实现TLS。只有执行线程可以访问其 TLS 变量 period,并且任何 "central thread" 都无法查看其他线程的 TLS 变量。它由您的操作系统内核强制执行。 "central thread" 必须与每个线程合作,并使用典型的线程间通信方式安排访问线程的 TLS 变量。
cookie 方法可以很好地工作,并且您不需要使用 TLS 槽来实现它,只需在您的线程过程中使用一个局部变量即可。要处理 cookie 在线程创建时间和线程启动时间之间更改值的情况 运行(有一个小延迟),您必须将当前 cookie 值作为输入参数传递给线程创建,然后您的线程过程可以在开始检查活动 cookie 的更改之前将其局部变量初始化为该值。
intptr_t g_cookie = 1;
pthread_rwlock_t g_lock;
void* thread_proc(void *arg)
{
intptr_t cookie = (intptr_t)arg;
while (keepRunningUntilSomeCondition)
{
pthread_rwlock_rdlock(&g_lock);
if (cookie != g_cookie)
{
cookie = g_cookie;
s_IsAllAboutThatBass = true;
}
pthread_rwlock_unlock(&g_lock);
//...
}
pthread_exit(NULL);
}
void createThread()
{
...
pthread_t thread;
pthread_create(&thread, NULL, &thread_proc, (void*)g_cookie);
...
}
void signalThreads()
{
pthread_rwlock_wrlock(&g_lock);
++g_cookie;
pthread_rwlock_unlock(&g_lock);
}
int main()
{
pthread_rwlock_init(&g_lock, NULL);
// use createThread() and signalThreads() as needed...
pthread_rwlock_destroy(&g_lock);
return 0;
}
这是 "Plan A" 使用 C++11 标准 atomic
variable and thread_local
-specifier 的轻量级实现。 (如果您的编译器不支持它们,请替换为供应商特定设施。)
#include <atomic>
struct Foo {
static std::atomic<unsigned> s_TokenGeneration;
static thread_local unsigned s_LocalToken;
static thread_local bool s_LocalState;
// for central thread
void signalResetIsAllAboutThatBass() {
++s_TokenGeneration;
}
// accessor for other threads
void setIsAllAboutThatBass(bool b) {
unsigned currToken = s_TokenGeneration;
s_LocalToken = currToken;
s_LocalState = b;
}
bool getIsAllAboutThatBass() const {
unsigned currToken = s_TokenGeneration;
if (s_LocalToken < currToken) {
// reset thread-local token & state
s_LocalToken = currToken;
s_LocalState = true;
}
return s_LocalState;
}
};
std::atomic<unsigned> Foo::s_TokenGeneration;
thread_local unsigned Foo::s_LocalToken = 0u;
thread_local bool Foo::s_LocalState = true;