我为什么要使用特定于线程的数据?

时间:2012-06-19 12:19:05

标签: c multithreading pthreads

由于每个线程都有自己的堆栈,因此可以将其私有数据放在其上。例如,每个线程可以分配一些堆内存来保存一些数据结构,并使用相同的接口来操作它。然后为什么特定于线程的数据有用

我能想到的唯一情况是,每个线程可能有多种私有数据。如果我们需要访问该线程中调用的任何函数中的私有数据,我们需要将数据作为参数传递给所有这些函数,这很无聊且容易出错。

9 个答案:

答案 0 :(得分:4)

线程局部存储是避免全局状态的解决方案。如果数据不是跨线程共享,而是由多个函数访问,则可以将其设置为线程本地。无需担心打破重入。使调试更容易。

从性能的角度来看,使用线程本地数据是一种避免虚假共享的方法。假设你有两个线程,一个负责写入变量 x ,另一个负责从变量 y 读取。如果要将这些定义为全局变量,它们可能位于同一缓存行中。这意味着如果其中一个线程写入 x ,CPU将更新缓存行,这当然包括变量 y ,因此缓存性能会降低,因为没有理由更新 y

如果使用线程本地数据,则一个线程只存储变量 x ,另一个线程只存储变量 y ,从而避免错误共享。但请记住,还有其他方法可以解决这个问题,例如:缓存行填充。

答案 1 :(得分:2)

与堆栈(与线程本地数据专用于每个线程)不同,线程局部数据很有用,因为它通过函数调用持久存储(与堆栈数据不同,如果在函数之外使用它可能已被覆盖)。

另一种方法是使用专用于每个线程的相邻全局数据,但是当涉及CPU缓存时,这会产生一些性能影响。由于不同的线程可能在不同的内核上运行,因此全局数据的这种“共享”可能会带来一些不希望的性能下降,因为来自一个内核的访问可能使另一个内核的高速缓存行无效,后者有助于更多的内容。核心流量,以确保缓存一致性。

相反,使用线程本地数据在概念上不应该涉及搞乱其他内核的缓存。

答案 2 :(得分:2)

将线程本地存储视为另一种全局变量。它是全局的,你不需要传递它,不同的代码可以随意访问它(当然是声明)。但是,每个不同的线程都有自己独立的变量。通常,全局变量在多线程编程中是非常糟糕的,因为其他线程可以更改值。如果你使它成为本地线程,只有你的线程可以看到它,所以另一个线程不可能意外地改变它。

另一个用例是当您被迫使用(设计糟糕的)API时,该API希望您使用全局变量将信息传递给回调函数。这是一个被强制转换为全局变量的简单实例,但是使用线程本地存储使其成为线程安全的。

答案 3 :(得分:2)

好吧,我已经编写了30多年的多线程应用程序,从来没有发现任何使用TLS的需求。如果一个线程需要一个DB绑定到该线程的数据库连接,那么该线程可以打开它自己的一个并将其保留在堆栈中。由于线程无法调用,只能发出信号,没有问题。每次我看过这个神奇的'TLS',我都意识到这不是我问题的解决方案。

通过我典型的消息传递设计,对象排队到永不终止的线程,不需要TLS。

使用线程池它甚至更无用。

我只能说使用TLS =糟糕的设计。如果可以的话,有人会说我的权利:)

答案 4 :(得分:0)

我已经将线程本地存储用于数据库连接,有时用于请求/响应对象。举两个例子,都来自Java webapp环境,但原则仍然存在。

Web应用程序可能包含大量代码,可调用各种子系统。其中许多可能需要访问数据库。就我而言,我编写了每个子系统,要求db从数据库池获取数据库连接,使用连接,并将连接返回到池。线程本地存储提供了一种更简单的替代方法:创建请求时,从池中获取数据库连接并将其存储在线程本地存储中。然后,每个子系统只使用来自线程本地存储的数据库连接,当请求完成时,它返回与数据库池的连接。这个解决方案具有性能优势,同时也不需要我通过每个级别的数据库连接:即我的参数列表保持较短。

在同一个Web应用程序中,我在一个远程子系统中决定我实际上想要查看Web Request对象。所以我要么重构要将这个对象一直传递下来,这将涉及大量的参数传递和重构,或者我可以简单地将对象放入线程本地存储中,并在我需要时检索它。

在这两种情况下,你可能会说我首先搞砸了设计,并且只是使用Thread Local存储来保存我的培根。你可能有一点意见。但我还可以争辩说,Thread Local可以提供更清晰的代码,同时保持线程安全。

当然,我必须非常肯定我在Thread Local中所做的事情确实是每个线程中唯一的一个。对于Web应用程序,Request对象或数据库连接很好地符合此描述。

答案 5 :(得分:0)

我想补充一下上面的答案,据我所知,性能明智,堆栈上的分配比堆上的分配更快。

关于跨调用传递本地数据,好吧 - 如果你在堆上分配,你需要将指针/引用(我是一个Java人:)传递给调用 - 否则,你将如何访问内存?

TLS也很好,以便存储在线程内的调用之间传递数据的上下文(我们使用它来保存跨线程的登录用户的信息 - 某种会话管理)。

答案 6 :(得分:0)

当特定线程的所有函数需要访问一个公共变量时,使用特定于线程的数据。此变量是该特定线程的本地变量,但作为该线程的所有函数的全局变量。

让我们说我们有两个线程t1和t2的任何进程。变量' a'是t1的线程特定数据。然后,t2不知道' a'但是t1的所有功能都可以访问'作为全球变量。任何变化都是' a'将被t1的所有功能所见。

答案 7 :(得分:0)

使用新的OOP技术,我发现线程特定的数据是无关紧要的。您可以传递函数,而不是将函数传递给线程。您传递的仿函数类可以保存您需要的任何特定于线程的数据。

EG。使用C ++ 11或boost的示例代码如下所示

MyClassFunctor functorobj;       <-- Functor Object. Can hold the function that runs as part of thread as well as any thread specific data
boost::thread mythread(functorobj);



Class MyClassFunctor
{
private:
 std::list mylist; <-- Thread specific data
public:
    void operator () ()
    {
      // This function is called when the thread runs
      // This can access thread specific data mylist.
    }
};

答案 8 :(得分:0)

  • Allows each thread to have its own copy of data
  • May also be referred to as Thread Local Storage or Thread Static Variables
  • Useful when you do not have control over the thread creation process (i.e., when using a thread pool)