__thread Embarcadero c++ 10.1 中不创建特定于线程的变量

__thread in Embarcadero c++ 10.1 not creating thread specific variables

本文关键字:于线程 线程 变量 创建 c++ Embarcadero thread      更新时间:2023-10-16

在Embarcadero c ++ 10.1程序中,我正在使用"new"创建线程的多个实例。在每个实例中,我都需要包含指针和整数的线程特定变量。

我尝试了两种不同的声明:

static TProgram_Control __thread * ptrPC_local;
static int __thread i_local;

static __declspec(thread) TProgram_Control * ptrPC_local;
static __declspec(thread) int i_local;

我尝试使用和不使用"静态"修饰符。我看到了两个线程的行为,这些行为表明每个线程都在破坏这些变量的彼此值。

我在每个线程中添加了代码以打印出threadID和两个变量的地址

PrintItem("TProg_Control_Thread(" + String(MyPCNo) + "): &ptrPC_local 0x" + IntToHex((int)&ptrPC_local,8));
PrintItem("TProg_Control_Thread(" + String(MyPCNo) + "): &i_local 0x" + IntToHex((int)&i_local,8));
PrintItem("TProg_Control_Thread(" + String(MyPCNo) + "): ThreadID " + IntToHex((int)GetCurrentThreadId(), 8));

这显示在我的调试窗口中,对于线程 (1( 和线程 (2(:

TProg_Control_Thread(1): &ptrPC_local 0x00AC4788
TProg_Control_Thread(1): &i_local 0x00AC478C
TProg_Control_Thread(1): ThreadID 00001C34
TProg_Control_Thread(2): &ptrPC_local 0x00AC4788
TProg_Control_Thread(2): &i_local 0x00AC478C
TProg_Control_Thread(2): ThreadID 000014C4

似乎显示唯一的 ThreadID,但每个线程中两个变量的地址相同。

我是否正确理解了线程特定变量的概念?我是否应该看到每个线程中变量的不同地址,以便它们不会相互干扰?

像往常一样,我在这里得到了很好的建议。乌尔里希建议我尝试一个最小的可重现的例子。我花了一段时间,但我找到了一个编写Delphi测试用例的人。我用 C++ 重写了它,它完全符合我对线程局部变量的期望。

以下是代码(在Embarcadero C++控制台应用程序中(:

#include <vcl.h>
#include <condefs.h>
#pragma hdrstop
#include <stdio.h>
#include <Classes.hpp>
//---------------------------------------------------------------------------
// the thread local variable works with this declaration
__declspec(thread) char TestVar[256];
/* Output on console window:
main() calls Test().
Original TestVar at call to Test() (should be blank):
Address of TestVar : 005621F0
New TestVar at exit of Test(): Asdf0
Starting new TTestThread which calls Test().
Starting new TTestThread which calls Test().
Original TestVar at call to Test() (should be blank):
Address of TestVar : 0058F128
New TestVar at exit of Test(): Asdf1
Original TestVar at call to Test() (should be blank):
Address of TestVar : 0058F500
New TestVar at exit of Test(): Asdf2
TestVar at main() exit (should be Asdf0) Asdf0
*/
//---------------------------------------------------------------------------
// if I change the declaration as follows, program does not work correctly
//char TestVar[256];
/* Output on console window:
main() calls Test().
Original TestVar at call to Test() (should be blank):
Address of TestVar : 00421A60
New TestVar at exit of Test(): Asdf0
Starting new TTestThread which calls Test().
Starting new TTestThread which calls Test().
Original TestVar at call to Test() (should be blank): Asdf0
Address of TestVar : 00421A60
New TestVar at exit of Test(): Asdf1
Original TestVar at call to Test() (should be blank): Asdf1
Address of TestVar : 00421A60
New TestVar at exit of Test(): Asdf2
TestVar at main() exit (should be Asdf0) Asdf2
*/
//---------------------------------------------------------------------------
int i;
//---------------------------------------------------------------------------
void Test(void) {
printf("Original TestVar at call to Test() (should be blank): %sn", TestVar);
printf("Address of TestVar : %08Xn", &TestVar);
sprintf(TestVar, "Asdf%d", i);
i++;
printf("New TestVar at exit of Test(): %sn", TestVar);
}
//---------------------------------------------------------------------------
class TTestThread : public TThread
{
private:
protected:
void __fastcall Execute();
public:
__fastcall TTestThread(bool CreateSuspended);
};
//---------------------------------------------------------------------------
__fastcall TTestThread ::TTestThread (bool CreateSuspended)
: TThread(CreateSuspended)
{
FreeOnTerminate = true;
}
//---------------------------------------------------------------------------
void __fastcall TTestThread ::Execute()
{
Test();
}
//---------------------------------------------------------------------------
// test code based on post from [https://ibeblog.com/2010/08/17/delphi-thread-variables/][1]
// this was a piece of Delphi code which I rewrote in C++ for Borland
//
/* comments from that poster:
A somewhat less known feature of Delphi is thread variables. Thread variables
are variables that can contain a different value for each thread in the
application. They are defined like regular variables, the only difference is
that the “var” keyword is replaced with “threadvar”. In the example below I’ll
show you how they work in a small console application. Note that I used the
TThread class for convenience, however in reality you’ll only ever need this
for procedural code as you can add fields to your TThread subclass to store
data in for each thread. The example uses the main thread and a separated
thread to show you how it works.
01  program ThreadVarTest;
02
03  {$APPTYPE CONSOLE}
04
05  uses
06    SysUtils, Classes;
07
08  type
09    TTestThread = class(TThread)
10    protected
11      procedure Execute; override;
12    public
13      constructor Create;
14    end;
15
16  threadvar
17    TestVar: string;
18
19  var
20    i: Integer;
21
22  procedure Test;
23  begin
24    WriteLn('Original TestVar: ' + TestVar);
25    TestVar := 'Asdf' + IntToStr(i);
26    Inc(i);
27    WriteLn('New TestVar: ' + TestVar);
28  end;
29
30  { TTestThread }
31
32  constructor TTestThread.Create;
33  begin
34    inherited Create(False);
35    FreeOnTerminate := True;
36  end;
37
38  procedure TTestThread.Execute;
39  begin
40    Test;
41  end;
42
43  begin
44    i := 0;
45    Test;
46    WriteLn('Starting thread.');
47    TTestThread.Create;
48    ReadLn;
49    WriteLn(TestVar);
50    ReadLn; // Prevent window from closing
51  end.
As you can see when running it, even though the content of the thread variable
was changed, the variable was still empty when accessed by the new thread. I’ve
added the incremental number to show you that when after you press a key when
the thread has exited and you check the thread variable content for the main
thread, it will show Adsf0 as it was stored by the main thread originaly.
*/
//---------------------------------------------------------------------------
#pragma argsused
int main(int argc, char* argv[])
{
i = 0;
printf("main() calls Test().n");
Test(); // after the first call to Test, TestVar should be "Asdf0"
printf("Starting new TTestThread which calls Test().n");
// create thread
new TTestThread(false); // create and call Test()
// do it again
printf("Starting new TTestThread which calls Test().n");
new TTestThread(false); // create and call Test()
printf("TestVar at main() exit (should be Asdf0) %sn", TestVar);   // after 3 calls to Test, this TestVar should still be "Asdf0"
//
// All values for &TestVar should be different
//
return 0;
}
//---------------------------------------------------------------------------

所以我可以看到在 3 个线程中,每个线程都有一个不同的 TestVar 地址。

我回到了我的程序。结构看起来与TestVar程序一模一样。但是,当然,事实并非如此。我意识到我正在线程构造函数中的线程变量中设置值。在 Embarcadero C++ 环境中(也许无处不在(,构造函数在调用线程的上下文中运行。在每种情况下,这都是主线程,所以我的线程变量每次都设置为主线程的地址。

我将赋值移动到 Execute(( 函数中,现在我正确地为程序的每个线程中的线程变量获取了不同的地址。