Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to understand the static variables marked by ThreadStatic of C #

2025-03-01 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article focuses on "how to understand the static variables marked by the ThreadStatic of C#". Interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Now let the editor take you to learn "how to understand the static variables marked by the ThreadStatic of C #"!

The usage of ThreadStatic 1. Ordinary static variable

I believe that many friends have used static variables in their code, and it has many advantages. For example, I often use static to do a process-level cache to improve the performance of the program. Of course, you can also serve as a very good first-level cache, as shown in the following code:

Public class Test

{

Public static Dictionary cachedDict = new Dictionary ()

}

As I mentioned just now, this is a process-level cache that can be seen by multiple threads, so in a multi-threaded environment, you need to pay special attention to synchronization. Either use locks or use ConcurrentDictionary, I think this is also a mindset, very often thinking is always based on the existing basis to repair, to mend, but not to jump out of this thinking to deal with from the foundation, what does it mean to say so much? Let me give you an example:

In the common chain tracking framework in the market, such as Zikpin,SkyWalking, you can use some collections to store some link information that tracks the current thread, such as A-> B-> C-> D-> B-> A. the conventional way of thinking is to define a global cachedDict, and then use various synchronization mechanisms. In fact, you can also reduce the access scope of cachedDict and change the global access to Thread-level access. Isn't that a better solution?

two。 Mark static variables with ThreadStatic

To achieve Thread-level scope, it is very easy to implement, just type a ThreadStatic feature on cachedDict, and modify the code as follows:

Public class Test

{

[ThreadStatic]

Public static Dictionary cachedDict = new Dictionary ()

}

Next, you can open multiple threads to inject data into cachedDict to see if dict is Thread-level scope. The implementation code is as follows:

Class Program

{

Static void Main (string [] args)

{

Var task1 = Task.Run (() = >

{

If (Test.cachedDict = = null) Test.cachedDict = new Dictionary ()

Test.cachedDict.Add (1, "mary")

Test.cachedDict.Add (2, "john")

Console.WriteLine (dict of $"thread= {Thread.CurrentThread.ManagedThreadId} is recorded: {Test.cachedDict.Count}")

});

Var task2 = Task.Run (() = >

{

If (Test.cachedDict = = null) Test.cachedDict = new Dictionary ()

Test.cachedDict.Add (3, "python")

Test.cachedDict.Add (4, "jaskson")

Test.cachedDict.Add (5, "elen")

Console.WriteLine (dict of $"thread= {Thread.CurrentThread.ManagedThreadId} is recorded: {Test.cachedDict.Count}")

});

Console.ReadLine ()

}

}

Public class Test

{

[ThreadStatic]

Public static Dictionary cachedDict = new Dictionary ()

}

As a result, it is indeed a Thread level, and it also avoids the overhead of synchronization between threads. Haha, it is no wonder that some readers want to see how the underlying layer is implemented.

Use Windbg to dig ThreadStatic1. Knowledge of TEB and TLS TEB (Thread Environment Block)

Each thread has its own private data, which is placed in the TEB of Thread and can be printed in windbg if you want to see it.

0VOU 000 >! teb

TEB at 0000001e1cdd3000

ExceptionList: 0000000000000000

StackBase: 0000001e1cf80000

StackLimit: 0000001e1cf6e000

SubSystemTib: 0000000000000000

FiberData: 0000000000001e00

ArbitraryUserPointer: 0000000000000000

Self: 0000001e1cdd3000

EnvironmentPointer: 0000000000000000

ClientId: 0000000000005980. 0000000000005aa8

RpcHandle: 0000000000000000

Tls Storage: 000001b599d06db0

PEB Address: 0000001e1cdd2000

LastErrorValue: 0

LastStatusValue: c0000139

Count Owned Locks: 0

HardErrorMode: 0

From the structure of teb, we can see that there are not only thread local storage (TLS), but also exception-related information storage (ExceptionList) and other related information.

TLS (Thread Local Storage)

After startup, the process allocates a total of 1088 slots to TLS, each thread assigns a dedicated tlsindex index, and has a set of slots slots, which can be verified with windbg.

0VOU 000 >! tls

Usage:

Tls [teb]

Slot:-1 to dump all allocated slots

{0-0n1088} to dump specific slot

Teb: for current thread

0 for all threads in this process

(not threadid) to dump for specific thread.

0VOU 000 >! tls-1

TLS slots on thread: 5980.5aa8

0x0000: 0000000000000000

0x0001: 0000000000000000

0x0002: 0000000000000000

0x0003: 0000000000000000

0x0004: 0000000000000000

...

0x0019: 0000000000000000

0x0040: 0000000000000000

0VOL000 >! t Lock

DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception

01 5aa8 000001B599CEED90 2a020 Preemptive 000001B59B9042F8:000001B59B905358 000001b599cdb130 1 MTA

5 2 90c 000001B599CF4930 2b220 Preemptive 0000000000000015 0000000000000000 000001b599cdb130 0 MTA (Finalizer)

7 3 74 000001B59B7272A0 102a220 Preemptive 0000000000000015 0000000000000000 000001b599cdb130 0 MTA (Threadpool Worker)

9 4 2058 000001B59B7BAFF0 1029220 Preemptive 0000000000000000000000000000 000001b599cdb130 0 MTA (Threadpool Worker)

As you can see from the {0-0n1088} to dump specific slot above, there are always 1088 slots in the process, and the current main thread 5aa8 has 27 slot slots.

All right, the basic concepts are over, and now we're ready to analyze the assembly code.

two。 Find the answer from the assembly code

In order to better dig with windbg, I define a simple ThreadStatic int variable with the following code:

Class Program

{

[ThreadStatic]

Public static int i = 0

Static void Main (string [] args)

{

I = 10; / / 12 line

Var num = I

Console.ReadLine ()

}

}

Next, disassemble the code for the Main function with! U, and focus on the I = 10; of line 12.

0VUR 000 >! U / d 00007ffbe0ae0ffb

E:\ net5\ ConsoleApp5\ ConsoleApp5\ Program.cs @ 12:

00007ffb`e0ae0fd6 48b9b0fbb7e0fb7f0000 mov rcx,7FFBE0B7FBB0h

00007ffb`e0ae0fe0 ba01000000 mov edx,1

00007ffb`e0ae0fe5 e89657a95f call coreclrature Jittery GetSharedNonGCThreadStaticBase (00007ffc`40576780)

00007ffb`e0ae0fea c7401c0a000000 mov dword ptr [rax+1Ch], 0Ah

In terms of assembly instructions, the last 10 is assigned to the lower 32 bits of rax+1Ch, so where does the address of rax come from? You can see that the core logic is in the JIT_GetSharedNonGCThreadStaticBase method, so you need to take a look at what this method does.

3. Debug the core function JIT_GetSharedNonGCThreadStaticBase

Next, set a breakpoint at 12! bpmd Program.cs:12, the simplified assembly code for the method is as follows:

Coreclr!JIT_GetSharedNonGCThreadStaticBase:

00007ffc`2c38679a 448b0dd7894300 mov R9d, dword ptr [coreclrdisabled tlsdiagnostic index (00007ffc`2c7bf178)]

00007ffc`2c3867a1 654c8b042558000000 mov R8, qword ptr gs: [58h]

00007ffc`2c3867aa b908000000 mov ecx, 8

00007ffc`2c3867af 4f8b04c8 mov R8, qword ptr [r8+r9*8]

00007ffc`2c3867b3 4e8b0401 mov R8, qword ptr [rcx+r8]

00007ffc`2c3867b7 493b8060040000 cmp rax, qword ptr [r8ffc460h]

00007ffc`2c3867be 732b jae coreclrature JITTED GetSharedNonGCThreadStaticBasekeeper 0x6b (00007ffc`2c3867eb)

00007ffc`2c3867c0 4d8b8058040000 mov R8, qword ptr [r8ffc458h]

00007ffc`2c3867c7 498b04c0 mov rax, qword ptr [r8+rax*8]

00007ffc`2c3867cb 4885c0 test rax, rax

0x6b (00007ffc`2c3867ce 741b je coreclair JITTED GetSharedNonGCThreadStaticBasie (00007ffc`2c3867eb)

00007ffc`2c3867d0 8bca mov ecx, edx

00007ffc`2c3867d2 f644011801 test byte ptr [rcx+rax+18h], 1

00007ffc`2c3867d7 7412 je coreclrature Jitters GetSharedNonGCThreadStaticBasekeeper 0x6b (00007ffc`2c3867eb)

00007ffc`2c3867d9 488b4c2420 mov rcx, qword ptr [rsp+20h]

00007ffc`2c3867de 4833cc xor rcx, rsp

00007ffc`2c3867e1 e89a170600 call coreclringing cookie (00007ffc`2c3e7f80)

00007ffc`2c3867e6 4883c438 add rsp, 38h

00007ffc`2c3867ea c3 ret

Next I'll take a closer look at the mov operation here.

1) dword ptr [coreclrdom quota index (00007ffc`2c7bf178)]

This is very simple, get the thread-specific tls_index index

2) qword ptr gs: [58h]

What does gs: [58h] mean here? Some friends should know that the gs register is the teb address specifically used to store the current thread, and the following 58 represents the offset on the teb address, so the question is, who does this address point to? In fact, you can print out the data structure of teb and understand it.

0RV 000 > dt teb

Coreclr!TEB

+ 0x000 NtTib: _ NT_TIB

+ 0x038 EnvironmentPointer: Ptr64 Void

+ 0x040 ClientId: _ CLIENT_ID

+ 0x050 ActiveRpcHandle: Ptr64 Void

+ 0x058 ThreadLocalStoragePointer: Ptr64 Void

+ 0x060 ProcessEnvironmentBlock: Ptr64 _ PEB

...

The above sentence + 0x058 ThreadLocalStoragePointer: Ptr64 Void shows that it actually points to ThreadLocalStoragePointer.

3) qword ptr [r8+r9*8]

With the basis of the first two steps, this assembly is very simple, it does an index operation: ThreadLocalStoragePoint [TLS _ index], whether it is correct, so as to get the tls content that belongs to the thread, and the variables of this ThreadStatic will be stored in some memory block of this array.

At this point, I believe you have a deeper understanding of "how to understand the static variables marked by the ThreadStatic of C #". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report