Threads completed, but the loop does not end

I wrote some thread code with what seems like the wrong assumption that integers were thread safe. Now it seems that, although they are, my use of them is NOT thread safe. I use a global ThreadCount integer to store the number of threads. During thread creation, I increase ThreadCount. During the destruction of the stream, I reduce it. After all threads are created, I wait for them to execute (ThreadCount should drop to 0), and then write my final report and exit.

Sometimes (5%), although I never get 0, although a post-mortem study of my journal shows that all threads were running and ending. Thus, all signs point to a totalization of ThreadCount. I told myself that this is not possible since it is an integer and I just use inc / dec.

Here is the code:

var // global ThreadCount : integer; // Number of active threads ... constructor TTiesUpsertThread.Create(const CmdStr : string); begin inherited create(false); Self.FreeOnTerminate := true; ... Inc(ThreadCount); // Number of threads created. Used for throttling. end; destructor TTiesUpsertThread.Destroy; begin inherited destroy; Dec(ThreadCount); // When it reaches 0, the overall job is done. end; ... //down at the end of the main routine: while (ThreadCount > 0) do // Sometimes this doesn't ever end. begin SpinWheels('.'); // sleeps for 1000ms and writes dots... to console end; 

I THINK that my problem is with inc / dec. I think I get collisions when two or more dec () hit at the same time and both read the same value, so they replace it with the same value. ex: ThreadCount = 5, and two threads terminate simultaneously, both are read 5, replaced by 4. But the new value should be 3.

This never encounters difficulties in our test environment (other equipment, topology, loading, etc.), so I am looking for confirmation that this is probably a problem before I try to โ€œsellโ€ this solution for a business unit.

If this is my problem, am I using critical selection to protect inc / dec?
Thanks for watching.

+4
source share
1 answer

If multiple threads modify a variable without protection, then yes, you have a data race. If two threads try to increase or decrease the same instance, then the following happens:

  • The variable is read into register.
  • Modification is made in the register.
  • The new value returns to the variable.

That read / modify / write is not atomic. If you have two threads running at the same time, you have a canonical data race.

  • Topic 1 reads the meaning, N says.
  • In stream 2, the value is read, the same value that was read by thread 1, N.
  • Topic 1 writes N + 1 to a variable.
  • Topic 2 writes N + 1 to a variable.

And instead of a variable increasing twice, it only increases once.

In this case, there is no need for a full-scale critical section. Use InterlockedIncrement to perform an unsecured, thread-safe modification.

+7
source

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


All Articles