Delphi中threadvar的作用是什么?

13

当我们声明一个threadvar变量时,这个变量何时被初始化(即对象被创建)? 它是在第一次变量赋值时发生吗?例如:

threadvar
  myThreadVar : string;

......

//inside a thread
  ...
  myThreadVar := 'my value'; // In this point the var will be initialized?

如果在线程设置完变量的值后,我尝试在线程之外使用该变量会发生什么?例如:

//at the main thread (application)
  ...
  //Call the thread;
  //thread finishes execution
  //thread is destroyed
  ShowMessage(myThreadVar); // what happens here?
3个回答

18

当线程首次访问任一线程变量时,其线程变量将被初始化。它们被设置为默认的全零值,对于字符串而言则是空字符串。

线程变量可能会被终止,也可能不会。这取决于运行时库得到线程终止通知的时间。因此,最好不要将任何动态分配类型(包括字符串)存储在线程变量中。相反,使用一个TThread对象的实例变量来存储线程特定数据。


你问题的第二部分是无意义的。它要求你在线程已经终止后在线程上执行代码。不存在在"线程外"运行代码的情况。所有的代码都在线程中运行。每个程序至少有一个线程。

每个线程都有其自己的线程变量副本。没有线程可以读取另一个线程的副本,因此一旦一个线程终止,其所有线程变量都将无法访问。

你的ShowMessage调用将显示属于当前线程的值,而不是已经终止的线程。


4
我认为他所说的“线程外”是指“在我给它分配值的线程之外”,而不是“完全不在任何线程中”。但是你说得对,这反映了对线程变量本质的基本误解。 - Mason Wheeler
1
如果您查看SysInit.pas中的ExitThreadTLS,您会发现虽然RTL将释放支持threadvars的内存,但没有尝试对其内容进行终结。因此,任何留在threadvar中的托管变量(字符串、接口、动态数组、变体)都将泄漏。 - Thorsten Engler
感谢您的指针,@Thorsten。请注意,当一个Delphi DLL从线程中分离时,该函数被调用。Delphi EXE不会得到通知。 - Rob Kennedy
这是唯一一个释放线程变量内存的地方。我猜这意味着,如果您不断启动访问线程变量的新线程,然后在Delphi exe(而不是dll)中终止,那么迟早会耗尽内存。 - Thorsten Engler
“没有线程可以读取另一个线程的副本”——这并不是真的,你可以获取线程本地变量的地址,并通过这种方式访问其内存,所有内存都在同一进程范围内的地址空间中。 - Wolf

7
线程本地存储将在创建线程时被清零(初始化)。因此,在运行myThreadVar := 'my value';这一行之前,它将是一个空字符串。
至于您的第二个问题,线程变量对每个线程都是唯一的。当您声明一个线程变量时,您声明了线程本地存储中的一个插槽,并且每个线程都会获得插槽的副本。您可以将其视为thread1.myThreadVarthread2.myThreadVarmainThread.myThreadVar等。因此,如果您在一个线程中设置了一个线程变量,并尝试在另一个线程中读取它,则不会读取您在另一个线程中设置的内容;而是读取当前线程版本的线程变量分配的任何内容。

请问您能否提供“线程本地存储将在创建线程时被清零(初始化)”的参考资料(官方文档或官方源代码)?我已经尝试过了,但未能找到。 - Moacir Schmidt

3

threadvar表示您在每个线程中都有一个变量实例。不存在“不在线程内”的概念 - 如果您没有在创建的显式线程内运行,则正在运行进程的默认线程。如果在显式线程内设置了线程变量的值,则所有其他线程都看不到该值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接