473,785 Members | 2,698 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Spot the Bug: Fun Concurrency Bug

I hit this bug last night in some test code I had written. It took a few
minutes to figure out what the root cause was, and it left me thinking,
"Wow. That was an interesting one that doesn't come up very often!".

So, for fun, who sees the bug and can explain why it's happening?

List<Threadthre ads = new List<Thread>();
for (int i = 0; i < 2; i++)
{
Thread t = new Thread(delegate ()
{
Debug.Assert(i != 2);
});

t.Start();
threads.Add(t);
}

foreach (Thread thread in threads)
thread.Join();

--
Chris Mullins
Oct 24 '07
19 1314
Jon,

I do not understand much of threading & was curious
So, I ran this using snippet compiler & couldn't see any difference in
output

<code>
using System;
using System.Collecti ons.Generic;
using System.Diagnost ics;
using System.Threadin g;

class Sample
{
public static void Main(string[] args)
{
List<Threadthre ads = new List<Thread>();
for (int i = 0; i < 2; i++)
{
int j = i;
Thread t = new Thread(delegate ()
{
Console.WriteLi ne("inside thread: " + j);
Debug.Assert(j != 2);
});

Console.WriteLi ne("starting thread: " + i);
t.Start();
threads.Add(t);

}

foreach (Thread thread in threads)
thread.Join();
}
}
</code>

Could you explain, what is the problem with having to introduce j as a
temp. variable?
What is the problem with the original code (without introduction of
temp. variable inside the loop)?

Thanks
Kalpesh
On Oct 24, 12:28 pm, Jon Skeet [C# MVP] <sk...@pobox.co mwrote:
Chris Mullins [MVP - C#] <cmull...@yahoo .comwrote:
I hit this bug last night in some test code I had written. It took a few
minutes to figure out what the root cause was, and it left me thinking,
"Wow. That was an interesting one that doesn't come up very often!".
So, for fun, who sees the bug and can explain why it's happening?
List<Threadthre ads = new List<Thread>();
for (int i = 0; i < 2; i++)
{
Thread t = new Thread(delegate ()
{
Debug.Assert(i != 2);
});
t.Start();
threads.Add(t);
}
foreach (Thread thread in threads)
thread.Join();

Posting without reading any responses... "i" is captured and only has a
single "instance", so the assertion will fail if the thread executes
after the loop has "finished".

The solution:

List<Threadthre ads = new List<Thread>();
for (int i = 0; i < 2; i++)
{
int j=i; // THIS IS THE CHANGE
Thread t = new Thread(delegate ()
{
Debug.Assert(j != 2); // AND THIS
});

t.Start();
threads.Add(t);

}

foreach (Thread thread in threads)
thread.Join();

There's a new instance of "j" each time we go round the loop.

--
Jon Skeet - <sk...@pobox.co m>http://www.pobox.com/~skeet Blog:http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Oct 24 '07 #11
"Jon Skeet [C# MVP]" <sk***@pobox.co mwrote in message
news:MP******** *************@m snews.microsoft .com...
Chris Mullins [MVP - C#] <cm******@yahoo .comwrote:
>I hit this bug last night in some test code I had written. It took a few
minutes to figure out what the root cause was, and it left me thinking,
"Wow. That was an interesting one that doesn't come up very often!".

So, for fun, who sees the bug and can explain why it's happening?

List<Threadthr eads = new List<Thread>();
for (int i = 0; i < 2; i++)
{
Thread t = new Thread(delegate ()
{
Debug.Assert(i != 2);
});

t.Start();
threads.Add(t);
}

foreach (Thread thread in threads)
thread.Join();

Posting without reading any responses... "i" is captured and only has a
single "instance", so the assertion will fail if the thread executes
after the loop has "finished".

The solution:

List<Threadthre ads = new List<Thread>();
for (int i = 0; i < 2; i++)
{
int j=i; // THIS IS THE CHANGE
Thread t = new Thread(delegate ()
{
Debug.Assert(j != 2); // AND THIS
});

t.Start();
threads.Add(t);
}

foreach (Thread thread in threads)
thread.Join();

There's a new instance of "j" each time we go round the loop.
Or:
for (int i = 0; i < 2; i++)
{
Thread t = new Thread((Paramet erizedThreadSta rt)delegate(obj ect
y){ Debug.Assert((i nt)y != 2); });
t.Start(i);
threads.Add(t);
}

Or in C# V3 :
for (int i = 0; i < 2; i++)
{
Thread t = new Thread(y = { Debug.Assert((i nt)y != 2); });
t.Start(i);
threads.Add(t);
}
Willy.

Oct 24 '07 #12
Kalpesh <sh*********@gm ail.comwrote:

<snip>
Could you explain, what is the problem with having to introduce j as a
temp. variable?
What is the problem with the original code (without introduction of
temp. variable inside the loop)?
It's not really a concurrency issue - it's an anonymous method issue.
The two new threads (and the continuing method) are all still using the
same variable.

See http://pobox.com/~skeet/csharp/csharp2/delegates.html for more -
look at the last section (captured variables)

--
Jon Skeet - <sk***@pobox.co m>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Oct 24 '07 #13
I'm not sure that Sleep nor a loop would help here - really I suspect
all you need is:

for (int i = 0; i < 2; i++)
{
int j = i;
Thread t = new Thread(delegate ()
{
Debug.Assert(j != 2);
});
t.Start();
threads.Add(t);
}

By 1.1 standards it looks the same, but j should (IIRC) now be scoped
(and hence captured) *inside* the loop. On each successive loop, the j
is completely different. Without the capture, of course, there is only
a single j declared on the stack at the top of the method... gotta
love capture ;-p

Marc

Oct 24 '07 #14
ahh, frick; sorry Jon - I didn't see your (scarily similar) post.
Deferred posting... oops and apols...

Oct 24 '07 #15
Marc Gravell wrote:
I'm not sure that Sleep nor a loop would help here - really I suspect
all you need is:
Help with what? The post to which I replied asked if there was a way to
_ensure_ that the two different versions of the code behaved
differently. I don't know why he's unable to reproduce a difference
without changing the samples; assuming he's running on Windows, it's
hard to imagine a scenario where the thread creating the other threads
gets preempted before the i loop exits.

But taking as granted his statement is true and he does in fact have
trouble seeing the difference, there are ways to hack up the code so
that the timing is more deterministic.

Please don't think that the post to which you replied was intended to
address the original issue. My comments there are _strictly_ with
respect to manipulating the code so that it fails in a more obvious,
reproducible way.

Pete
Oct 24 '07 #16
AJ

As a follow-on question... where does variable i 'live'. It's local and
a value type, so lives on the local stack...

So... remove the thread.Join() and make the threads take a little longer
e.g.

new Thread( delegate() { Thread.Sleep(50 00); Debug.Assert(i! =2); }

The original method will have completed before the thread runs its code.
So where is the 'i' that it's referencing? Won't the stack have changed
by then?


In article <e6************ **@TK2MSFTNGP02 .phx.gbl>, cm******@yahoo. com
says...
I hit this bug last night in some test code I had written. It took a few
minutes to figure out what the root cause was, and it left me thinking,
"Wow. That was an interesting one that doesn't come up very often!".

So, for fun, who sees the bug and can explain why it's happening?

List<Threadthre ads = new List<Thread>();
for (int i = 0; i < 2; i++)
{
Thread t = new Thread(delegate ()
{
Debug.Assert(i != 2);
});

t.Start();
threads.Add(t);
}

foreach (Thread thread in threads)
thread.Join();

--
Chris Mullins
Oct 25 '07 #17
As a follow-on question... where does variable i 'live'. It's local
and
a value type, so lives on the local stack...
Nope, it doesn't live on the stack - it is "captured", so it lives as
a field on an object that the C# compiler creates for you. The rules
for the scoping of each "captured" variable is quite tricky... but but
*conceptually* what we are talking about (without using captures)
would be comparable to the following (although the compiler implements
it differently):

class SomeObject {
int i;
void SomeMethod() {
Debug.Assert(i != 2);
}
}
....
SomeObject obj = new SomeObject(); // obj instance is managed
for(int tmp = 0; tmp < 2; tmp ++) { // tmp lives on the stack
obj.i = tmp; // i lives on obj
Thread t = new Thread(obj.Some Method);
}

note that there is only a single "obj", and hence all share an "i".
Now compare to the version with a "j" introduced (see my other post in
this topic):

class SomeOtherObject {
int j;
void SomeOtherMethod () {
Debug.Assert(j != 2);
}
}
....
for(int i = 0; i < 2; i++) { // i lives on the stack
SomeOtherObject obj = new SomeOtherObject (); // obj instance is
managed
obj.j = i; // j lives on obj
Thread t = new Thread(obj.Some OtherObject);
}

in this latter case, each loop iteration gets a different object, and
hence a different "j"

Please note: I have simplified the behavior to make a simple example.

Marc
Oct 25 '07 #18
On Oct 25, 10:47 am, AJ <no...@nowhere. comwrote:
As a follow-on question... where does variable i 'live'. It's local and
a value type, so lives on the local stack...
Well, it's only *sort* of a local variable. It's actually a captured
variable, and will live on the heap. It's the fact that it's captured
that makes the whole thing confusing.

Jon

Oct 25 '07 #19
Chris Mullins [MVP - C#] wrote:
Your explination is dead on, but the boxing / byref behavior of the
variable passed into the Closure is what made this code behavie in a way
other than was expected. I was expecting the int (a value type) to be
passed in by value, and therefore not change...
Actually, this functionality is much more useful. Besides the explanations
given here, this page may be interessting to you:

http://en.wikipedia.org/wiki/Closure_(computer_science)

Regards,

Mads

--
Med venlig hilsen/Regards

Systemudvikler/Systemsdevelope r cand.scient.dat , Ph.d., Mads Bondo
Dydensborg
Dansk BiblioteksCente r A/S, Tempovej 7-11, 2750 Ballerup, Tlf. +45 44 86 77
34
Oct 29 '07 #20

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

16
2901
by: aurora | last post by:
Hello! Just gone though an article via Slashdot titled "The Free Lunch Is Over: A Fundamental Turn Toward Concurrency in Software" http://www.gotw.ca/publications/concurrency-ddj.htm]. It argues that the continous CPU performance gain we've seen is finally over. And that future gain would primary be in the area of software concurrency taking advantage hyperthreading and multicore architectures. Perhaps something the Python interpreter...
3
2449
by: Suzanne | last post by:
Hi All I'm having problems getting my data adapter to throw a concurrency exception with an INSERT command. I want to throw a concurrency exception if an attempt is made to enter a row into tb_table when a row with the same int_UID already exists in there. Here is my stored procedure: if not exists (select int_UID from tb_table where int_UID = @aint_UID)
4
1560
by: Bob | last post by:
While testing my my program I came up with a consistency exception. My program consists of three datagridviews, One called dgvPostes which is the parent grid and its two children,one called dgvPlans and the other dgvTanks. What happens is as follows. I will either create or edit a record in the datagridview dgvPlans and call the Updatedb procedure (code below). The first save works OK. Then when that is done, on the same record I will try...
7
1772
by: William E Voorhees | last post by:
I'm updating an Access database in a windows multi-user environment. I'm using disconnected data I read data from an Access Data table to a data object I update the data object from a windows form I save the data from the data object to the Access Data table using a data adapter as follows:
3
1431
by: John | last post by:
Hi I have a vs 2003 winform data app. All the data access code has been generated using the data adapter wizard and then pasted into the app. The problem I have is that I am getting a data concurrency error on mydataadapter.update() method. I know that there is no data concurrency problem as I am the only user testing the app. Obviously the error is misleading. What can I do from here to fix this problem? Thanks
19
1487
by: Chris Mullins [MVP - C#] | last post by:
I hit this bug last night in some test code I had written. It took a few minutes to figure out what the root cause was, and it left me thinking, "Wow. That was an interesting one that doesn't come up very often!". So, for fun, who sees the bug and can explain why it's happening? List<Threadthreads = new List<Thread>(); for (int i = 0; i < 2; i++) { Thread t = new Thread(delegate()
5
1852
by: John | last post by:
Hi I have developed the following logic to handle db concurrency violations. I just wonder if someone can tell me if it is correct or if I need a different approach.Would love to know how pros handle it. Thanks Regards
0
9480
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10327
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10151
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
0
9950
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
8973
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7499
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5381
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5511
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
4053
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system

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.