0

我在多线程 C++11 软件中有 2 种类似情况:

  • 我在方法声明中用作查找表的数组
  • 一个数组,我用作在方法外部声明的查找表,并且通过引用或指针被不同的多个方法使用。

现在,如果我们暂时忘记这个 LUT,我们只考虑 C++11 和通用方法的多线程方法,那么就存储持续时间而言,这种方法最合适的限定符可能是thread_local.

这样,如果我将一个方法foo()提供thread_local给 3 个线程,我基本上最终foo()每个线程都有 3 个实例,这一举措“解决”了foo()在 3 个不同线程之间共享和访问的问题,避免缓存丢失,但我基本上有 3 个我的 foo() 可能有不同的行为,例如,如果我在 foo() 中实现了相同的 PRNG,并且我提供了一个与时间相关且分辨率非常好的种子,那么每个线程我可能会得到 3 个不同的结果并且在一致性方面确实一团糟。

但是假设我对工作方式很好,我thread_local如何写下我需要保持 LUT 始终准备好并为我的方法缓存的事实?

我读过一些关于宽松或不那么宽松的内存模型的内容,但在 C++11 中,我从未见过可以注入数组/LUT 缓存的关键字或实际应用程序。

我在 x86 或 ARM 上。

我可能需要一些与volatile基本相反的东西。

4

2 回答 2

3

如果 LUT 是只读的,那么您可以在没有锁的情况下共享它们,您应该只使用其中一个(即声明它们static)。

线程没有自己的缓存。但即使他们这样做了(核心通常有自己的 L1 缓存,并且您可能能够将线程锁定到核心),两个不同的线程缓存同一内存结构的不同部分也不会有问题。

“线程本地存储”并不意味着内存在物理上与线程相关联。相反,这是一种让相同名称在每个线程中引用不同对象的方法。如果给定它的地址,它绝不会限制任何线程访问该对象的能力。

于 2013-07-01T04:45:44.143 回答
1

CPU 高速缓存不可编程。它使用自己的内部逻辑来确定要缓存哪些内存区域。通常,它会缓存 CPU 刚刚访问过的内存,或者其预测逻辑确定 CPU 很快会访问的内存。在多处理器系统中,每个 CPU 可能有自己的缓存,或者不同的 CPU 可能共享一个缓存。如果有多个缓存,一个内存区域可能同时缓存在多个缓存中。

如果所有线程必须在查找表中看到相同的值,那么最好使用单个表。这可以通过具有static存储持续时间的变量来实现。如果可以修改数据,那么您可能还需要std::mutex保护对表的访问并避免数据竞争。无需额外同步即可共享只读数据;在这种情况下,最好声明它const以明确只读性质并避免意外修改。

void foo(){
     static const int lut[]={...};
}

你使用thread_localwhere 每个线程必须有自己的数据副本,通常是因为每个副本都会被独立修改。例如,您可以选择使用thread_local随机数生成器,这样每个线程都有自己的 RNG,它独立于其他线程,并且不需要同步。

void bar(){
    thread_local RandomNumberGenerator rng; // one per thread
    auto val=rng.nextRandomNumber(); // use the instance for the current thread
}
于 2013-07-02T10:57:37.840 回答