我有一个由许多DLL文件组成的游戏.其中一些DLL链接到相同的静态库(LIB).
所以像这样:
Game.exe -> Root.dll -> Child.dll
| |
| '-> Common.lib (contains __declspec(thread))
|
'-> Common.lib (contains __declspec(thread))
Run Code Online (Sandbox Code Playgroud)
Root.dll加载静态链接Common.lib的Child.dll.Root还静态链接Common.lib.因为Common是静态链接的,所以它会直接编译到加载dll中(例如Root和Child).
Common.lib包含使用线程本地存储(TLS)的变量.
__declspec(thread) static void* s_threadValues[PlatformThreadSlotsMax];
Run Code Online (Sandbox Code Playgroud)
这会导致一些有问题的行为: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);
});
Run Code Online (Sandbox Code Playgroud)
Nodejs域显式绑定的更多解释
在controller/service中 - process.domain将为您提供上面创建的域并且您可以轻松地将值绑定到此域.例如:
process.domain.obj = {};
Run Code Online (Sandbox Code Playgroud)
这种解释足以理解域的用法.
问题
将域用于多个请求是否安全?
如何确保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
Run Code Online (Sandbox Code Playgroud)
所以第一次按每个线程调用它时,值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 …Run Code Online (Sandbox Code Playgroud) 我刚刚遇到这种技术,每个线程运行一次代码.我不知道它是如何在最低级别工作的.特别是,fs指向什么?什么.zero 8意思?标识符是否有原因@tpoff?
int foo();
void bar()
{
thread_local static auto _ = foo();
}
Run Code Online (Sandbox Code Playgroud)
输出(带-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
Run Code Online (Sandbox Code Playgroud) 我正在阅读文档部分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;
}
Run Code Online (Sandbox Code Playgroud)
然后在不同的线程中(例如)
thread_local_get<float>() += 1.f;
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
仅在 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
Run Code Online (Sandbox Code Playgroud)
ARM32平台不支持preserve_most属性(https://godbolt.org/z/SQRJB2):
<source>:2:21: warning: 'preserve_most' calling convention …Run Code Online (Sandbox Code Playgroud) 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;
}
Run Code Online (Sandbox Code Playgroud)
来自 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
Run Code Online (Sandbox Code Playgroud)
所以我的期望是 FS 的实际值会因每个线程而改变。例如,在调试器下, gdb: info regor p $fs,我会看到 FS 的值在不同的线程中是不同的,但没有: ds, es, fs, gs 一直都为零。
在我自己的代码中,我写了类似下面的内容并得到相同的结果 - FS 没有改变,但 TLV “有效”:
struct …Run Code Online (Sandbox Code Playgroud) 我有使用 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);
}
Run Code Online (Sandbox Code Playgroud)
在分析我的代码时,我注意到 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
Run Code Online (Sandbox Code Playgroud)
这些调用大大减慢了循环速度。我可以手动使用此版本并参考:
lea rbx, -20[rbp]
data16 lea rdi, f()::buffer@tlsgd[rip]
.value 0x6666
rex64
call __tls_get_addr@PLT ; <- This call!
mov …Run Code Online (Sandbox Code Playgroud) 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