threads, static variables

K

kracchh

Hi,

the following .NET 1.1 C#-program does not terminate (opposed to what I
would expect):

--------
using System;
using System.Threading;

public class Test {

static int i = 0;

public static void Main(string [] args) {
new Thread(new ThreadStart(t1)).Start();
new Thread(new ThreadStart(t2)).Start();
}

static void t1() {
int j = 0;
while(i == 0) {
j++;
}
}

static void t2() {
i = 1;
}

}
--------

A slight variation of the above program makes the strange behavior more
explicit:

--------
using System;
using System.Threading;

public class Test {

static int i1 = 0;
static int i2 = 0;

public static void Main(string [] args) {
new Thread(new ThreadStart(t1)).Start();
new Thread(new ThreadStart(t2)).Start();
}

static void t1() {
int j = 0;
i1 = 1;
while(i2 == 0) {
j++;
if(j + 1 < j) break;
}
Console.WriteLine(j);
}

static void t2() {
i2 = 1;
Console.WriteLine("t2: i1={0}, i2={0}", i1, i2);
}

}
--------
Output:
--------
t2: i1=1, i2=1
2147483647
--------

That means thread t2 sets the static variable i2=1, but thread t1 seems
to be unaware of this change of i2.

Can anyone reproduce this behavior in his environment with the above
programs (don't change a single line, because even slight variations
result in a different behavior)?

And what I'm interested more: Can anyone explain why these programs
behave in this way? Is this a bug in .NET/C#?
 
J

Jon Skeet [C# MVP]

the following .NET 1.1 C#-program does not terminate (opposed to what I
would expect):

That means thread t2 sets the static variable i2=1, but thread t1 seems
to be unaware of this change of i2.
Yup.

Can anyone reproduce this behavior in his environment with the above
programs (don't change a single line, because even slight variations
result in a different behavior)?

It doesn't happen on my box, but that's a single processor box - are
you by any chance running it on a multi-processor box?
And what I'm interested more: Can anyone explain why these programs
behave in this way? Is this a bug in .NET/C#?

No, it's not a bug in .NET. It's the way the .NET memory model works.
See http://www.pobox.com/~skeet/csharp/threads/volatility.shtml
 
W

Willy Denoyette [MVP]

Inline

Willy.

Jon Skeet said:
It doesn't happen on my box, but that's a single processor box - are
you by any chance running it on a multi-processor box?
The first sample won't terminate even on a single CPU when compiled with
full JIT optimization turned on (/o+ ), the static variable is register
allocated as opposed to heap allocated when not optimized (the default).
 
K

kracchh

Hi,
It doesn't happen on my box, but that's a single processor box - are
you by any chance running it on a multi-processor box?

One Pentium-4 with hyperthreading turned off.
No, it's not a bug in .NET. It's the way the .NET memory model works.
See http://www.pobox.com/~skeet/csharp/threads/volatility.shtml

Thanks a lot for that URL. Declaring i and i2 as "volatile" helped!

The first sample won't terminate even on a single CPU when compiled with
full JIT optimization turned on (/o+ ), the static variable is register
allocated as opposed to heap allocated when not optimized (the default).

"/o+" or "/o-" made no difference on my computer (first sample didn't
terminate in both cases).
 
W

Willy Denoyette [MVP]

Hi,


One Pentium-4 with hyperthreading turned off.


Thanks a lot for that URL. Declaring i and i2 as "volatile" helped!



"/o+" or "/o-" made no difference on my computer (first sample didn't
terminate in both cases).

Try with /debug option on v1.1.4322, it should terminate. In this release
/o- is the default, and I guess it does register allocation per default
unless you have /debug specified.


Willy.
 
J

Jon Skeet [C# MVP]

Willy Denoyette said:
The first sample won't terminate even on a single CPU when compiled with
full JIT optimization turned on (/o+ ), the static variable is register
allocated as opposed to heap allocated when not optimized (the default).

Ooh, indeed. That's great - a real sample I could include in my page,
which positively shows the dangers!

Cheers.
 
W

Willy Denoyette [MVP]

Jon Skeet said:
Ooh, indeed. That's great - a real sample I could include in my page,
which positively shows the dangers!

Sure it is, note that I'm experimenting a bit with these settings, and there
seem to be a minor difference between v1.1 and v2 (the beta). In v2.0 /o- ,
keeps the static heap allocated, while /o+ does register allocation (in this
particular sample!). In V1.1 /o+ or /o- both use a register for the
variable. Moreover, that I notice this behavior is processor type dependent,
that is AMD Athlon64 and Intel P4 behave differently.
You see reasons enough to lock your shared state.
 

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