Threading - issues

J

Jarod

Hi!

I've done some code using threading just to check if writing code improperly
can cause serious issues when using somehow safe data types.

So in my understanding a stack ( collection type ) won't give any item more
than once. But it does !!!

Again I wrote this code improperly to see what happens but I don't
understand why it happens so if you understand the issue only than reply ;)

private void btnTestThreading_Click(object sender, EventArgs e)
{
Stack<int> numbersStack = new Stack<int>();
for (int i = 0; i < 100; i++)
numbersStack.Push(i);

for (int i = 0; i < 10; i++)
{
Thread th = new Thread(delegate()
{
// i'm putting this as a reference so it should be
shared nicely
TestThreading(ref numbersStack, i);
}
);

th.Start();
}


}

private void TestThreading(ref Stack<int> numbers, int threadNumber)
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(numbers.Peek() * 10);
Console.WriteLine("Thread with number " + threadNumber +
"\t\t processed item " + numbers.Pop());
}
}


In my understanding the worst case scenario here would be 2 threads
accessing stack in the same time ( i'm not using any locks ) and than the
same item would be 'processed' twice. But what happens is 1 thread is getting
the same item twice...
HOW it's possible ? I would understand if it was other thread but it's the
same one. If you delete Thread.Sleep it stops ( at least at mine ;)

Thanks
Jedrzej
 
M

Marc Gravell

I suspect you are victim of "captured variables" on "i";
try the following:

for (int i = 0; i < 10; i++)
{
int tmp = i; // **THIS IS IMPORTANT
Thread th = new Thread(delegate()
{
TestThreading(ref numbersStack, tmp);
}
);

th.Start();
}

I believe they were different threads, but with the same identifer ;-p

I can explain in a second (just got "visited" by someone)

Marc
 
J

Jarod

I believe they were different threads, but with the same identifer ;-p
And you was right ;)
Could you explain how this happens that 2 diffrent threads accessing same
reference in this case stack are getting same results.
When 1 object invokes Pop() it should remove an item from the stack they are
accessing same place in the memory... There is probably a silly explanation
:)
I need an explanation of how it works to get better understanding :)

Thanks
Jedrzej
 
J

Jon Skeet [C# MVP]

And you was right ;)
Could you explain how this happens that 2 diffrent threads accessing same
reference in this case stack are getting same results.
When 1 object invokes Pop() it should remove an item from the stack they are
accessing same place in the memory... There is probably a silly explanation
:)
I need an explanation of how it works to get better understanding :)

Well, does Stack claim to be thread-safe? Most .NET collections don't
make that claim - if you access the same collection from multiple
threads with no locking, I'd expect to see some oddness. Do you get
the same issues when you obtain a lock while looking at the
collection?

Also, I think you have some misunderstandings about what "ref" does -
see http://pobox.com/~skeet/csharp/parameters.html

Jon
 
J

Jarod

Well, does Stack claim to be thread-safe? Most .NET collections don't
make that claim - if you access the same collection from multiple
threads with no locking, I'd expect to see some oddness. Do you get
the same issues when you obtain a lock while looking at the
collection?

Jon,

I have problem with understanding how it's possible that using reference I
can get same element from stack twice when definition of the stack says it's
impossible.
I don't understand what exactly happens when 2 threads are accessing exactly
the same object which is reference and than they are trying to invoke method
Pop() which gives you an element and deletes it! So it looks like some other
thread was able to get the same element before deleting it. I'm just trying
to understand how it's possible.

Congrats on your book :)
Jedrzej
 
L

Lasse Vågsæther Karlsen

Jarod said:
Jon,

I have problem with understanding how it's possible that using reference I
can get same element from stack twice when definition of the stack says it's
impossible.
I don't understand what exactly happens when 2 threads are accessing exactly
the same object which is reference and than they are trying to invoke method
Pop() which gives you an element and deletes it! So it looks like some other
thread was able to get the same element before deleting it. I'm just trying
to understand how it's possible.

Congrats on your book :)
Jedrzej

Let's assume the Pop method of the Stack class does the following:

1. Read the top element into a local variable
2. Remove the top element from its internal list
3. Return the element it read

If two threads manage to execute step 1 at the same time before neither
is at step 2, then both threads will of course see the same object.

In addition, depending on how Pop is implemented, if one of the threads
manage to run step 2 to completion before the other thread started step
2, these two threads might end up both seeing the same object, and also
removing the top two objects, one of which won't be seen by any thread.
 
I

Ignacio Machin ( .NET/ C# MVP )

I suspect you are victim of "captured variables" on "i";
try the following:

             for (int i = 0; i < 10; i++)
             {
                 int tmp = i; // **THIS IS IMPORTANT
                 Thread th = new Thread(delegate()
                     {
                         TestThreading(ref numbersStack, tmp);
                     }
                 );

                 th.Start();
             }

I believe they were different threads, but with the same identifer ;-p

I can explain in a second (just got "visited" by someone)

Marc

Tricky indeed :)
 
I

Ignacio Machin ( .NET/ C# MVP )

Well, does Stack claim to be thread-safe? Most .NET collections don't
make that claim - if you access the same collection from multiple
threads with no locking, I'd expect to see some oddness. Do you get
the same issues when you obtain a lock while looking at the
collection?

Also, I think you have some misunderstandings about what "ref" does -
seehttp://pobox.com/~skeet/csharp/parameters.html

Jon

Worth to notice is the existence of a method Synchronized in several
collections ( Queue, Stack, ArrayList) that wrap the collection in a
synced version (of course you always have to access it throught the
synced one)

So to get a multithread safe Stack you can do

Stack s = Stack.Synchronized( new Stack() );
 
I

Ignacio Machin ( .NET/ C# MVP )

Jon,

I have problem with understanding how it's possible that using reference I
can get same element from stack twice when definition of the stack says it's
impossible.
I don't understand what exactly happens when 2 threads are accessing exactly
the same object which is reference and than they are trying to invoke method
Pop() which gives you an element and deletes it! So it looks like some other
thread was able to get the same element before deleting it. I'm just trying
to understand how it's possible.

Congrats on your book :)
Jedrzej

Hi,

Very easy indeed,
do this. make two sequences of how an item is returned from the stack,
assume this operations
1- call to pop
2- asssign elem to temp var
3- delete from list
4- return elem

now, what happen if two threads are at step 2 at the same time????
 
M

Marc Gravell

Could you explain how this happens that 2 diffrent threads accessing same
reference in this case stack are getting same results.

Sorry - I got side-tracked.
The 2 different threads getting the same item is what we expect
without synchronization (actually, the whole thing could just explode
in a huge exception). The other respondants have already covered this
in plenty of detail.

The issue with your original code giving multiple threads the same id
is (as mentioned) a side-effect of "captured variables". Jon covers it
well in section 5.5 of "C# in Depth", but a brief version is that
"there is only 1 i"... even inside the ThreadStart they are all the
same variable*. By declaring "tmp" /inside/ the brace, its capture is
scoped by the brace - so each iteration of the "for" loop has a /
different/ tmp**.
This behaviour is a side-effect of capturing the variable in an
anonymous method / lambda. And it only bites when doing things like
threading, or passing the delegate outside of the method.

Marc

*=actually a field on an instance of a compiler-generated class
**=actually, a different instance of the compiler-generated class,
hence separate fields
[see I told you it was complex...]
 
Q

qglyirnyfgfo

Fascinating,

So in short, it looks like when the compiler noticed that the “temp”
variable was inside the loop, it went on and created a compiler
generated class where the “anonymous” function and “temp” variable
reside.

During execution, the compiler instantiated this class once per
iteration saving the “temp” value there.

Finally the thread calls the anonymous method on that instance using
the “temp” value.

Sweeeeeeet, at first, I thought that the code wouldn’t work because I
didn’t know the compiler was going to instantiate the compiler
generated class one time *per* iteration.

So the question that begs to be asked is (at least I am begging!)…..
What triggers the compiler to create and instantiate the class one
time per iteration??? I wanted to see what would happen if I created
my own loop so I change the code as follows:

int j = 0;
LoopLabel:

int temp = j;
Thread th = new Thread(delegate()
{
TestThreading(numbersStack, temp);
}
);

th.Start();

if (++j < 10)
goto LoopLabel;

That didn’t work, so I went ahead and added scope brackets to see what
would happen and it worked, see code below:

int j = 0;
LoopLabel:
{
int temp = j;
Thread th = new Thread(delegate()
{
TestThreading(numbersStack, temp);
}
);

th.Start();

if (++j < 10)
goto LoopLabel;
}

I guess the compiler figure out that the label was causing a loop to
happen so it did the right thing.

Well, I realize that what I am doing its kind of pointless but for
some reason I find this very interesting, can someone point me to the
document that would spell out what triggers the compiler to act
differently for the different scenarios? Probably the ECMA document
somewhere on page 154,534,665 :)

Anyone wanting to share any interesting experiences regarding
“captured variables”?

Thanks.
Rene
 
M

Marc Gravell

As Peter mentions, it is the "scope" that matters (braces in this
case). Jon's book gives a lot more detail - worth picking up a copy ;-
p Chapter 5 (which covers this) isn't one of the freebies ones,
though...

Marc
 

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