如何从另一个线程读取/写入线程局部变量?也就是说,在线程A中,我想访问线程B的线程本地存储区域中的变量。我知道另一个线程的ID。
变量在GCC中被声明为__thread
。目标平台是Linux,但独立性可能会更好(不过特定于GCC也可以)。
由于缺少线程开始钩子,我无法在每个线程开始时简单地跟踪此值。所有线程都需要以这种方式跟踪(不仅仅是专门启动的线程)。
更高级别的 Package 器,如boost thread_local_storage或使用pthread键都不是一个选择。我需要使用真正的__thread
局部变量的性能。
- 第一个答案是错误的 *:不能用全局变量来做我想做的事情。每个线程都必须有自己的变量副本。此外,出于性能原因,这些变量必须是
__thread
变量(同样有效的解决方案也可以,但我不知道)。我也不控制线程入口点,因此这些线程不可能 * 注册 * 任何类型的结构。 - Thread Local不是私有的 *:关于线程局部变量的另一个误解。这些绝对不是线程的某种 * 私有 * 变量。它们是全局可寻址的内存,但有一个限制,即它们的生存期与线程有关。来自任何线程的任何函数,如果给它们一个指向这些变量的指针,就可以修改它们。上面的问题本质上是关于如何获得指针地址。
5条答案
按热度按时间mv1qrgav1#
如果你想要线程局部变量而不是线程局部变量,为什么不使用全局变量呢?
重要说明!
我并不是建议您使用单个全局变量来替换线程局部变量。我建议使用一个全局 * 数组 * 或其他合适的值集合来替换一个线程局部变量。
当然,您必须提供同步,但由于您希望将在线程A中修改的值公开给线程B,因此无法绕过这个问题。
更新:
__thread
上的GCC文档说:当address-of运算符应用于线程局部变量时,它在运行时计算并返回该变量的当前线程示例的地址。这样获得的地址可以由任何线程使用。当线程终止时,该线程中任何指向线程局部变量的指针都将无效。
因此,如果你坚持这样做,我想有可能在线程生成之后,从它所属的线程中获取线程局部变量的地址。然后,您可以将指向该内存位置的指针存储到一个Map中(线程id => pointer),并让其他线程以这种方式访问该变量。这假定您拥有派生线程的代码。
如果你真的很喜欢冒险,你可以尝试挖掘关于
___tls_get_addr
的信息(从前面提到的GCC文档链接的this PDF开始)。但是这种方法是高度特定于编译器和平台的,并且缺乏文档,因此它应该引起任何人的警觉。gr8qqesn2#
我在寻找同样的东西。正如我看到没有人回答你的问题后,搜索了网络上的所有方式,我到达后续信息:假设在Linux(Ubuntu)上编译GCC并使用-m64,则段寄存器gs保持值0。段的隐藏部分(保存线性地址)指向线程特定的局部区域。该区域在该地址处包含该地址的地址(64位)。在较低的地址存储所有线程局部变量。地址是
native_handle()
。因此,为了访问线程的本地数据,您应该通过该指针进行访问。换句话说:
(char*)&variable-(char*)myThread.native_handle()+(char*)theOtherThread.native_handle()
上面的代码演示了假设g++,Linux,pthreads是:
3z6pesqy3#
这是一个老问题,但既然没有给出答案,为什么不使用一个拥有自己静态注册的类呢?
当然,你需要在线程之间进行某种同步,以确保线程已经注册了指针,但是你可以从任何你知道线程id的线程的map中获取它。
dxxyhpgq4#
很不幸,我从来没有找到一种方法来做到这一点。
如果没有某种类型的线程初始化钩子,似乎就没有办法到达那个指针(除了依赖于平台的ASM黑客)。
mbyulnm05#
这几乎是你所需要的,如果不修改你的要求。
在Linux上,它使用
pthread_key_create
,windows使用TlsAlloc
。它们都是通过 key 检索本地线程的一种方式。然而,如果你注册了键,你就可以在其他线程上访问数据.EnumerableThreadLocal的思想是在线程中执行本地操作,然后在主线程中减少结果。
tbb有一个类似的函数,名为可执行线程特定的,它的动机可以在https://oneapi-src.github.io/oneTBB/main/tbb_userguide/design_patterns/Divide_and_Conquer.html中找到
下面是一个尝试,模仿tbb代码,而不依赖于tbb。下面的代码的缺点是你在Windows上被限制为1088个键。
以及一组测试案例来展示它是如何工作的