C # ThreadStatic + mutable elements not working properly

I read the post tips and tricks , and I thought I would try some of the C # stuff that I had never done before. Therefore, the following code does not serve the actual purpose, but merely is a “test function” to see what happens.

Anyway, I have two static private fields:

private static volatile string staticVolatileTestString = ""; [ThreadStatic] private static int threadInt = 0; 

As you can see, I am testing ThreadStaticAttribute and the volatile keyword.

Anyway, I have a validation method that looks like this:

 private static string TestThreadStatic() { // Firstly I'm creating 10 threads (DEFAULT_TEST_SIZE is 10) and starting them all with an anonymous method List<Thread> startedThreads = new List<Thread>(); for (int i = 0; i < DEFAULT_TEST_SIZE; ++i) { Thread t = new Thread(delegate(object o) { // The anon method sets a newValue for threadInt and prints the new value to the volatile test string, then waits between 1 and 10 seconds, then prints the value for threadInt to the volatile test string again to confirm that no other thread has changed it int newVal = randomNumberGenerator.Next(10, 100); staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " setting threadInt to " + newVal; threadInt = newVal; Thread.Sleep(randomNumberGenerator.Next(1000, 10000)); staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " finished: " + threadInt; }); t.Start(i); startedThreads.Add(t); } foreach (Thread th in startedThreads) th.Join(); return staticVolatileTestString; } 

What I expect to see from this function is as follows:

 thread 0 setting threadInt to 88 thread 1 setting threadInt to 97 thread 2 setting threadInt to 11 thread 3 setting threadInt to 84 thread 4 setting threadInt to 67 thread 5 setting threadInt to 46 thread 6 setting threadInt to 94 thread 7 setting threadInt to 60 thread 8 setting threadInt to 11 thread 9 setting threadInt to 81 thread 5 finished: 46 thread 2 finished: 11 thread 4 finished: 67 thread 3 finished: 84 thread 9 finished: 81 thread 6 finished: 94 thread 7 finished: 60 thread 1 finished: 97 thread 8 finished: 11 thread 0 finished: 88 

However, I get the following:

 thread 0 setting threadInt to 88 thread 4 setting threadInt to 67 thread 6 setting threadInt to 94 thread 7 setting threadInt to 60 thread 8 setting threadInt to 11 thread 9 setting threadInt to 81 thread 5 finished: 46 thread 2 finished: 11 thread 4 finished: 67 thread 3 finished: 84 thread 9 finished: 81 thread 6 finished: 94 thread 7 finished: 60 thread 1 finished: 97 thread 8 finished: 11 thread 0 finished: 88 

The second half is output as expected (which, I suppose, means the ThreadStatic field works as I thought), but it looks like some of the initial outputs were skipped from the first half.

In addition, the threads in the first half are out of order, but I understand that the thread does not start right away, as soon as you call Start (); but instead, internal OS controls will trigger threads as they see fit. EDIT: No, they are not, in fact, I just thought it was because my brain skips consecutive numbers


So my question is: what's wrong with making me lose a few lines in the first half? For example, where does the string 'thread 3 set threadInt to 84'?

+6
source share
2 answers

Threads are performed simultaneously. What fundamentally happens is:

 staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " setting threadInt to " + newVal; 
  • Topic 1 reads staticVolatileTestString
  • Topic 2 reads staticVolatileTestString
  • Topic 3 reads staticVolatileTestString
  • Topic 1 adds stuff and writes staticVolatileTestString back
  • Topic 2 adds stuff and writes staticVolatileTestString back
  • Topic 3 adds stuff and writes staticVolatileTestString back

This will result in the loss of your lines. Volatiles do not help here; the whole string concatenation process is not atomic. You need to use the lock on these operations:

 private static object sync = new object(); lock (sync) { staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " setting threadInt to " + newVal; } 
+7
source

MSDN describes what the volatile keyword does here :

The volatile keyword indicates that a field can be modified by multiple simultaneous threads. Fields declared volatile do not fall under compiler optimization, which assume access by a single thread. This ensures that the most modern value is constantly present in the field.

This means that what happens in your example (may change from time to time, depends on the scheduler):

  • stream 0 reads a string from staticVolatileTestString
  • thread 0 adds 'thread 0 setting threadInt to 88'
  • thread 0 writes the string back to staticVolatileTestString

this seems to be expected, but then:

  • thread 1-4 reads a string from staticVolatileTestString
  • thread 1 adds 'thread 1 setting threadInt to 97'
  • thread 2 adds 'thread 2 setting threadInt to 11'
  • thread 2 writes the string back to staticVolatileTestString
  • ... threads 1, 2, 3 read and add and write
  • thread 4 writes the string back to staticVolatileTestString
  • etc.

see what happened here? thread 4 reads the line "thread 0 setting threadInt to 88", adds "thread 4 ..." and writes it back, overwriting all lines 1, 2 and 3 are already written to the line.

+2
source

Source: https://habr.com/ru/post/917867/


All Articles