471,325 Members | 1,824 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 471,325 software developers and data experts.

Threading - issues

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<intnumbersStack = 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<intnumbers, 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

Jun 27 '08 #1
12 1308
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
Jun 27 '08 #2
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
Jun 27 '08 #3
On May 30, 12:22 pm, Jarod <Ja...@discussions.microsoft.comwrote:
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 :)
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
Jun 27 '08 #4
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
Jun 27 '08 #5
Jarod wrote:
>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
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.

--
Lasse Vågsæther Karlsen
mailto:la***@vkarlsen.no
http://presentationmode.blogspot.com/
PGP KeyID: 0xBCDEA2E3
Jun 27 '08 #6
On May 30, 6:49*am, Marc Gravell <marc.grav...@gmail.comwrote:
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 :)
Jun 27 '08 #7
On May 30, 8:16*am, "Jon Skeet [C# MVP]" <sk...@pobox.comwrote:
On May 30, 12:22 pm, Jarod <Ja...@discussions.microsoft.comwrote:
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 theyare
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 -
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() );

Jun 27 '08 #8
On May 30, 9:21*am, Jarod <Ja...@discussions.microsoft.comwrote:
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
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????
Jun 27 '08 #9
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...]
Jun 27 '08 #10
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 wouldnt work because I
didnt 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 didnt 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
Jun 27 '08 #11
On Fri, 30 May 2008 14:08:09 -0700, <qg**********@mailinator.comwrote:
[...]
I guess the compiler figure out that the label was causing a loop to
happen so it did the right thing.
The compiler doesn't care about the label at all. It's the fact that a
new scope, defined by the braces, is entered that's relevant. Every time
the code enters a new level of scoping, variables declared within that
scope are considered "new" with respect to capturing.
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 :)
I didn't look it up in the ECMA document, but in the most recent C# 3.0
specification (why there's not an ECMA spec yet I don't know), it's
documented in section 7.14.4.2, "Instantiation of local variables". The
instantiation rules are what cause local variables that are captured to be
reinstantiated within a scope that's re-entered, but not otherwise.
Anyone wanting to share any interesting experiences regarding
“captured variables”?
No, not really. :) Actually, other than the fact that it's a reasonably
common error to fail to take into account the capturing and instantiation
rules, I don't have any personal experience that might be considered
"interesting".

Pete
Jun 27 '08 #12
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
Jun 27 '08 #13

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

13 posts views Thread by Varun | last post: by
1 post views Thread by Your Friend | last post: by
21 posts views Thread by Illumineo | last post: by
1 post views Thread by James T | last post: by
16 posts views Thread by One Handed Man \( OHM - Terry Burns \) | last post: by
reply views Thread by Colmeister | last post: by
4 posts views Thread by DBC User | last post: by
3 posts views Thread by Richard MSL | last post: by
5 posts views Thread by George Maicovschi | last post: by
126 posts views Thread by Dann Corbit | last post: by
reply views Thread by rosydwin | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.