我有一个由许多DLL文件组成的游戏.其中一些DLL链接到相同的静态库(LIB).
所以像这样:
Game.exe -> Root.dll -> Child.dll
               |            |
               |            '-> Common.lib (contains __declspec(thread))
               |
               '-> Common.lib (contains __declspec(thread))
Root.dll加载静态链接Common.lib的Child.dll.Root还静态链接Common.lib.因为Common是静态链接的,所以它会直接编译到加载dll中(例如Root和Child).
Common.lib包含使用线程本地存储(TLS)的变量.
__declspec(thread) static void* s_threadValues[PlatformThreadSlotsMax];
这会导致一些有问题的行为:Root.dll和Child.dll每个都包含一个不同的TLS数据实例(s_threadValues).即使在同一个线程上,如果Root.dll调用Common.lib中定义的函数,如果从Child.dll调用相同的函数,则s_threadValues的值将与其值不同.
由于两个DLL都是从同一个线程访问这个TLS,我希望共享TLS,但事实并非如此.
现在,如果我将Common.lib更改为动态链接(例如Common.dll),则不会再出现此问题:对于Root.dll和Child.dll,s_threadValues是相同的.
这是预期的行为吗?无论如何在动态库之间共享静态库中定义的TLS共享使用它吗?
c++ dll multithreading static-libraries thread-local-storage
我的用例要求node.js域在请求级别跨服务器文件共享信息.
express.js中的示例实现
domain = require('domain');
app.use(function(req, res, next) {
    var reqDomain = domain.create();
    reqDomain.add(req);
    reqDomain.add(res);
    reqDomain.run(next);
});
Nodejs域显式绑定的更多解释
在controller/service中 - process.domain将为您提供上面创建的域并且您可以轻松地将值绑定到此域.例如:
process.domain.obj = {};
这种解释足以理解域的用法.
问题
将域用于多个请求是否安全?
如何确保process.domain对于不同的请求是不同的而不是相同的?
我还想知道在继续本地存储中如何处理这些问题
javascript node.js thread-local-storage express node.js-domains
我正在使用依赖的第三方库thread_local.这导致我的程序__tls_init()重复调用,即使在某些周期的每次迭代中(我还没有检查所有这些),尽管thread_local变量已经被同一函数中早期的另一个调用无条件地初始化(事实上,在接近开始时)整个节目).
__tls_init()我的第一个指示x86_64是
cmpb    $0, %fs:__tls_guard@tpoff
je      .L530
ret
.L530:
pushq   %rbp
pushq   %rbx
subq    (some stack space), %rsp
movb    $1, %fs:__tls_guard@tpoff
所以第一次按每个线程调用它时,值at %fs:__tls_guard@tpoff设置为,1并且进一步调用立即返回.但是,这意味着call每次thread_local访问变量时都会产生所有开销,对吧?
请注意,这是一个静态链接(实际上是生成!)函数,因此编译器"知道"它从这个条件开始,并且完全可以想象流分析发现不必多次调用此函数.但事实并非如此.
是否有可能摆脱多余的call __tls_init指令,或者至少阻止编译器在时间关键的部分发出它们?
实际编译的示例情况:( - O3)
pushq   %r13
movq    %rdi, %r13
pushq   %r12
pushq   %rbp
pushq   %rbx
movq    %rsi, %rbx
subq    $88, %rsp
call    __tls_init              // always gets called
movq    (%rbx), %rdi
call    <some local function>
movq    8(%rax), %r12 …我刚刚遇到这种技术,每个线程运行一次代码.我不知道它是如何在最低级别工作的.特别是,fs指向什么?什么.zero 8意思?标识符是否有原因@tpoff?
int foo();
void bar()
{
    thread_local static auto _ = foo();
}
输出(带-O2):
bar():
        cmp     BYTE PTR fs:guard variable for bar()::_@tpoff, 0
        je      .L8
        ret
.L8:
        sub     rsp, 8
        call    foo()
        mov     BYTE PTR fs:guard variable for bar()::_@tpoff, 1
        add     rsp, 8
        ret
guard variable for bar()::_:
        .zero   8
我正在阅读文档部分boost::thread_specific_ptr,并尝试解析此段落:
注意:在某些平台上,不会对使用平台的本机API创建的线程执行特定于线程的数据的清理.在这些平台上,只有使用boost :: thread启动的线程才会进行此类清理,除非从该线程手动调用boost :: on_thread_exit().
首先,这可能是一个迂腐的观点:我认为他们的意思是说boost::this_thread::at_thread_exit()而不是boost::on_thread_exit().否则我真的迷路了.
更重要的是,线程究竟需要做什么?它是否足以将一些无操作函数传递给at_thread_exit(),还是需要传递其他东西?
(这个主题在这里的评论中讨论过,但我仍然不确定我需要做什么.)
(背景故事:我正在寻找解决我今天早些时候提出的问题的方法).
当我使用thread_local, _Thread_local, __thread, or 时__declspec(thread),编译器似乎在创建线程时分配一个线程本地存储,并将地址存储在 x86 派生系统中的fsorgs寄存器中。
在这种情况下,是否有“线程本地存储溢出”之类的东西?
这种thread_local存储持续时间的用法是否有任何警告:
template <class T>
inline T &thread_local_get()
{
  thread_local T t;
  return t;
}
然后在不同的线程中(例如)
thread_local_get<float>() += 1.f;
cppreference 上的文档说明了线程本地存储持续时间:
线程存储持续时间。对象在线程开始时分配,在线程结束时释放。每个线程都有自己的对象实例。只有声明为 thread_local 的对象具有此存储持续时间。thread_local 可以与 static 或 extern 一起出现以调整链接。
这是否thread_local为每个 T(编译期间)和每个调用线程正确分配了一个实例?是否有任何情况会导致例如未定义的行为?
关于编译器选项有几个问题。现在我使用以下内容:
-target i386-windows-gnu -mno-sse -c -O3
-target x86_64-windows-gnu -mcx16 -c -O3
-target i386-linux-gnu -mno-sse -c -O3
-target x86_64-linux-gnu -mcx16 -c -O3
-target i386-darwin-gnu -mno-sse -fomit-frame-pointer -c -O3
-target x86_64-macos-gnu -fomit-frame-pointer -c -O3
-target armv7-none-linux-androideabi -mfpu=neon -mfloat-abi=hard -mthumb -fPIC -c -O3
-target aarch64-linux-android -c -O3
-target armv7m-none-ios-gnueabi -mfpu=neon -mfloat-abi=hard -mthumb -c -O3
-target arm64-darwin-gnu -fno-stack-protector -c -O3
仅在 Linux/Android 上没有投诉。对于其他平台,它会发出警告(https://godbolt.org/z/YhZ5uc):
clang-9: warning: argument unused during compilation: '--gcc-toolchain=/opt/compiler-explorer/gcc-9.2.0' [-Wunused-command-line-argument]
Compiler returned: 0
ARM32平台不支持preserve_most属性(https://godbolt.org/z/SQRJB2):
<source>:2:21: warning: 'preserve_most' calling convention …macos multithreading clang compiler-options thread-local-storage
我阅读了许多文章和 S/O 答案说(在 linux x86_64 上)FS(或某些变体中的 GS)引用了一个特定于线程的页表条目,然后它给出了一个指向可共享的实际数据的指针数组数据。当线程被交换时,所有的寄存器都被切换,因此线程基页会发生变化。线程变量通过名称访问,只需要 1 个额外的指针跃点,并且引用的值可以共享给其他线程。一切都好,说得通。
事实上,如果你查看__errno_location(void)背后的函数的代码errno,你会发现类似的东西(这是来自 musl,但 gnu 并没有太大的不同):
static inline struct pthread *__pthread_self()
{
    struct pthread *self;
    __asm__ __volatile__ ("mov %%fs:0,%0" : "=r" (self) );
    return self;
}
来自 glibc:
=> 0x7ffff6efb4c0 <__errno_location>:   endbr64
   0x7ffff6efb4c4 <__errno_location+4>: mov    0x6add(%rip),%rax        # 0x7ffff6f01fa8
   0x7ffff6efb4cb <__errno_location+11>:        add    %fs:0x0,%rax
   0x7ffff6efb4d4 <__errno_location+20>:        retq
所以我的期望是 FS 的实际值会因每个线程而改变。例如,在调试器下, gdb: info regor p $fs,我会看到 FS 的值在不同的线程中是不同的,但没有: ds, es, fs, gs 一直都为零。
在我自己的代码中,我写了类似下面的内容并得到相同的结果 - FS 没有改变,但 TLV “有效”:
struct …我有使用 thread_local 缓冲区的代码,类似于:
int func() {
    thread_local std::vector<int> buffer;
    buffer.resize(0);
    for (int i = 0; i < 10000; i++) {
        buffer.push_back(i);
    }
    return processing(buffer);
}
在分析我的代码时,我注意到 gcc 在循环体内调用了一个调用_tls_get_addr(),以便访问buffer. 循环体的Godbolt反汇编如下所示:
        lea     rbx, -20[rbp]
        data16  lea rdi, f()::buffer@tlsgd[rip]
        .value  0x6666
        rex64
        call    __tls_get_addr@PLT  ; <- This call!
        mov     rsi, rbx
        mov     rdi, rax
        call    std::vector<int, std::allocator<int> >::push_back(int const&)@PLT
这些调用大大减慢了循环速度。我可以手动使用此版本并参考:
        lea     rbx, -20[rbp]
        data16  lea rdi, f()::buffer@tlsgd[rip]
        .value  0x6666
        rex64
        call    __tls_get_addr@PLT  ; <- This call!
        mov …c++ ×7
gcc ×2
assembly ×1
boost ×1
boost-thread ×1
c ×1
c++11 ×1
clang ×1
dll ×1
express ×1
g++ ×1
internal ×1
javascript ×1
linux ×1
macos ×1
node.js ×1
optimization ×1
singleton ×1
thread-local ×1
x86-64 ×1