在C ++中将所有TLS(线程本地存储)变量设置为新的单个值

时间:2015-02-12 23:34:02

标签: c++ multithreading asynchronous pthreads

我有一个类Foo,其中包含以下特定于线程的静态成员:

__declspec(thread) static bool s_IsAllAboutThatBass;

在实现文件中,它被初始化为:

__declspec(thread) bool Foo::s_IsAllAboutThatBass = true;

到目前为止一切顺利。现在,任何线程都可以在他们认为合适的情况下随意翻转bool。然后问题是:在某些时候我希望每个线程将bool重置为其初始true值。

如何从中心线程中将TLS的所有实例都归为真?

我已经想过用自己知道的同步原语来做这些的方法,比如关键部分,读/写部分或事件,但没有什么比这更合适了。在我的实际用例中我无法在任何相当长的时间内阻止任何其他线程。

感谢任何帮助。谢谢!

修改:计划A

一个想法是使用由所有线程读取并由中心线程写入的生成令牌 cookie 。然后,当通过某个访问器获取s_isAllAboutThatBass时,每个线程可以具有该线程查看的上一代的TLS。当线程本地cookie与共享cookie不同时,我们增加线程本地cookie并将s_isAllAboutThatBass更新为true

3 个答案:

答案 0 :(得分:3)

这是"计划A"的轻量级实施。使用C ++ 11标准atomic变量和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;

答案 1 :(得分:1)

最简单的答案是:你不能。它被称为线程本地存储的原因是因为只有它的线程可以访问它。根据定义,这意味着其他一些&#34;中心线程&#34;无法达到目的。根据定义,这就是它的全部内容。

现在,根据您的硬件和编译器平台如何实现TLS,如果您通过将TLS变量映射到不同的虚拟内存地址来实现TLS,则可能会有一个技巧。通常情况下,一个CPU寄存器是特定于线程的,它设置为指向不同的内存地址,所有TLS变量都作为相对地址访问。

如果是这种情况,你可以推导出一些线程安全的机制,每个线程通过这个机制获取一个指向其TLS变量的指针,并将它放入一个非TLS容器中,即你的&#34;中心线程& #34;可以到达。

当然,您必须将所有这些与线程保持同步,并在每个线程终止后进行清理。

您必须通过一个简单的测试来确定平台上是否存在这种情况:声明一个TLS变量,然后在两个不同的线程中比较它的指针地址。如果它不同,您可以以这种方式解决它。从技术上讲,这种指针比较是不可移植的,并且实现已经定义,但此时你已经远远没有实现特定的行为。

但是如果地址相同,则意味着您的实现使用虚拟内存寻址来实现TLS。只有正在执行的线程才能访问其TLS变量,句点,并且没有任何实际的方法可以使任何&#34;中心线程&#34;可以看看其他线程&#39; TLS变量。它由您的操作系统内核强制执行。 &#34;中心线程&#34;必须配合每个线程,并使用典型的线程间通信方式安排访问线程的TLS变量。

答案 2 :(得分:1)

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