在 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;