A Thread program that show wrong value

T

Tony Johansson

Hi!

At the bottom is a simple thread program.
If I run this program on a computer that doesn't have two kernels I always
get 100010 which is correct.
If I run this program on a computer that have two kernels I sometimes get a
value that could be 96542 or 97129 or something else that less that 100010.

The book explaination to this is the following.
"This happens because of the way that updating the Cound field works on the
processor. On most computers, this code resolve into three steps.
1. Load the value into a register inside the processor.
2. Increment the value in the register.
3. Copy the value from the register back to memory.
The problem is that in threaded code all these three steps are treated as
atomic operation. Two threads could each read the value from memory and at
the same time update them with the same updated value. This is why the code
just loses some counts.

Here is the MSDN docs explanation why you must for example use the
Interlocked solution
The Increment and Decrement methods increment or decrement a variable and
store the resulting value in a single operation. On most computers,
incrementing a variable is not an atomic operation, requiring the following
steps:
Load a value from an instance variable into a register.
Increment or decrement the value.
Store the value in the instance variable.
If you do not use Increment and Decrement, a thread can be preempted after
executing the first two steps. Another thread can then execute all three
steps. When the first thread resumes execution, it overwrites the value in
the instance variable, and the effect of the increment or decrement
performed by the second thread is lost.

My comment is that I would says that MSDN docs is correct saying
a thread can be preempted after executing the first two steps. Another
thread can then execute all three steps. When the first thread resumes
execution, it overwrites the value in the instance variable, and the effect
of the increment or decrement performed by the second thread is lost.

So my question is that is the book wrong when it say that Two threads could
each read the value from memory and at the same time update them with the
same updated value. This is why the code just loses some counts.
I mean that the pre-emptive multitasking mechanism is still being used so
you can only have one thread being executed while the other is waiting for a
timeslice.

Class Test
{
static void UpdateCount()
{
for (int x = 0; x <= 10000; x++)
Counter.count = Counter.count + 1;
}

public static void Main()
{
ThreadStart starter = new ThreadStart(UpdateCount);
Thread[] threads = new Thread[10];

for (int x = 0; x < 10; x++)
{
threads[x] = new Thread(starter);
threads[x].Start();
}

for (int x = 0; x < 10; x++)
{
threads[x].Join();
}

Console.WriteLine("Total: {0}", Counter.count);
Console.ReadLine();
}
}

//Tony
 
P

Peter Duniho

Rick said:
Tony said:
[...]
The book explaination to this is the following.
"This happens because of the way that updating the Cound field works
on the
processor. On most computers, this code resolve into three steps.
1. Load the value into a register inside the processor.
2. Increment the value in the register.
3. Copy the value from the register back to memory.
The problem is that in threaded code all these three steps are treated as
atomic operation. Two threads could each read the value from memory and at
the same time update them with the same updated value. This is why the code
just loses some counts.

[...]
So my question is that is the book wrong when it say that Two threads could
each read the value from memory and at the same time update them with the
same updated value. This is why the code just loses some counts.
I mean that the pre-emptive multitasking mechanism is still being used so
you can only have one thread being executed while the other is waiting for a
timeslice.

The book isn't wrong, just poorly worded. You should be used to that by
now.
[...]
This is why synchronization is often required when multiple threads are
sharing a global variable.

Synchronization of _some_ sort is always required when multiple threads
are sharing _any_ data, not just "global variables" (which don't really
exist in C#, though many people consider static class members as "global").

Pete
 
A

Arne Vajhøj

At the bottom is a simple thread program.
If I run this program on a computer that doesn't have two kernels I always
get 100010 which is correct.
If I run this program on a computer that have two kernels I sometimes get a
value that could be 96542 or 97129 or something else that less that 100010.

I will assume like other have that by kernels you really mean cores.

The program below could be shortened to:

Class Test
{
public static void Main()
{
Random rng = new Random();
Console.WriteLine("Total: {0}", rng.Next(100010+1));
}
}

It does not synchronize threads and if count is not volatile
then it may also have visibility problems.
The book explaination to this is the following.
"This happens because of the way that updating the Cound field works on the
processor. On most computers, this code resolve into three steps.
1. Load the value into a register inside the processor.
2. Increment the value in the register.
3. Copy the value from the register back to memory.
The problem is that in threaded code all these three steps are treated as
atomic operation.

Missing NOT ??
Two threads could each read the value from memory and at
the same time update them with the same updated value. This is why the code
just loses some counts.

Here is the MSDN docs explanation why you must for example use the
Interlocked solution
The Increment and Decrement methods increment or decrement a variable and
store the resulting value in a single operation. On most computers,
incrementing a variable is not an atomic operation, requiring the following
steps:
Load a value from an instance variable into a register.
Increment or decrement the value.
Store the value in the instance variable.
If you do not use Increment and Decrement, a thread can be preempted after
executing the first two steps. Another thread can then execute all three
steps. When the first thread resumes execution, it overwrites the value in
the instance variable, and the effect of the increment or decrement
performed by the second thread is lost.

That says the exact same thing.

Except that it also talks about the Interlocked.Increment and
Interlocked.Decrement methods which makes it an atomic update.
My comment is that I would says that MSDN docs is correct saying
a thread can be preempted after executing the first two steps. Another
thread can then execute all three steps. When the first thread resumes
execution, it overwrites the value in the instance variable, and the effect
of the increment or decrement performed by the second thread is lost.

So my question is that is the book wrong when it say that Two threads could
each read the value from memory and at the same time update them with the
same updated value. This is why the code just loses some counts.

I can't see the difference in substance.

Arne
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top