Re: When is "volatile& quot; used instead of "lock" ; ?
Peter Ritchie <google.ca@pete rritchie.comwro te:
Nah - I'll fix it just using an extra lock.
<snip>
But you've violated my conditions for correctness:
<quote>
The situation I've been talking about is where a particular variable is
only referenced *inside* lock blocks, and where all the lock blocks
which refer to that variable are all locking against the same
reference.
</quote>
In particular I said:
<quote>
Now I totally agree that *if* you start accessing the variable from
outside a lock block, all bets are off - but so long as you keep
everything within locked sections of code, all locked with the same
lock, you're fine.
</quote>
but by setting tester.continue Running outside the lock, you've gone
into the "all bets are off" territory.
Now, we don't want to start trying to acquire the lock that you've
already got: for one thing, the object reference is never available
outside ThreadEntry(), and for a second thing we appear to want to hold
that lock for a long time - we'd end up in a deadlock if the looping
thread held the lock and the thread which was trying to stop the loop
had to wait for the loop to finish before it could acquire the lock, if
you see what I mean.
So, we introduce a new lock to go round every access to
continueRunning . For ease of coding, we'll encapsulate continueRunning
in a property access, so the complete code becomes:
using System;
using System.Threadin g;
internal class Tester {
bool continueRunning = true;
object continueRunning Lock = new object();
public bool ContinueRunning {
get {
lock (continueRunnin gLock) {
return continueRunning ;
}
}
set {
lock (continueRunnin gLock) {
continueRunning = value;
}
}
}
public void ThreadEntry() {
int count=0;
Object locker = new Object();
lock (locker) {
while (ContinueRunnin g) {
count++;
}
}
}
}
public class Program {
static void Main() {
Tester tester = new Tester();
Thread thread = new Thread(new ThreadStart
(tester.ThreadE ntry));
thread.Name = "Job";
thread.Start();
Thread.Sleep(20 00);
tester.Continue Running = false;
}
}
It now works without any volatile variables (but plenty of volatile
reads and writes!).
--
Jon Skeet - <skeet@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
Peter Ritchie <google.ca@pete rritchie.comwro te:
Jon, it took me a while; but here's an example where lock doesn't work
and you *must* use volatile to get the correct behaviour:
and you *must* use volatile to get the correct behaviour:
<snip>
public class Program {
static void Main() {
Tester tester = new Tester();
Thread thread = new Thread(new ThreadStart(tes ter.ThreadEntry ));
thread.Name = "Job";
thread.Start();
>
Thread.Sleep(20 00);
>
tester.continue Running = false;
}
}
>
... uncomment the volatile on continueRunning and it runs as
expected, terminating after 2 seconds.
static void Main() {
Tester tester = new Tester();
Thread thread = new Thread(new ThreadStart(tes ter.ThreadEntry ));
thread.Name = "Job";
thread.Start();
>
Thread.Sleep(20 00);
>
tester.continue Running = false;
}
}
>
... uncomment the volatile on continueRunning and it runs as
expected, terminating after 2 seconds.
<quote>
The situation I've been talking about is where a particular variable is
only referenced *inside* lock blocks, and where all the lock blocks
which refer to that variable are all locking against the same
reference.
</quote>
In particular I said:
<quote>
Now I totally agree that *if* you start accessing the variable from
outside a lock block, all bets are off - but so long as you keep
everything within locked sections of code, all locked with the same
lock, you're fine.
</quote>
but by setting tester.continue Running outside the lock, you've gone
into the "all bets are off" territory.
Now, we don't want to start trying to acquire the lock that you've
already got: for one thing, the object reference is never available
outside ThreadEntry(), and for a second thing we appear to want to hold
that lock for a long time - we'd end up in a deadlock if the looping
thread held the lock and the thread which was trying to stop the loop
had to wait for the loop to finish before it could acquire the lock, if
you see what I mean.
So, we introduce a new lock to go round every access to
continueRunning . For ease of coding, we'll encapsulate continueRunning
in a property access, so the complete code becomes:
using System;
using System.Threadin g;
internal class Tester {
bool continueRunning = true;
object continueRunning Lock = new object();
public bool ContinueRunning {
get {
lock (continueRunnin gLock) {
return continueRunning ;
}
}
set {
lock (continueRunnin gLock) {
continueRunning = value;
}
}
}
public void ThreadEntry() {
int count=0;
Object locker = new Object();
lock (locker) {
while (ContinueRunnin g) {
count++;
}
}
}
}
public class Program {
static void Main() {
Tester tester = new Tester();
Thread thread = new Thread(new ThreadStart
(tester.ThreadE ntry));
thread.Name = "Job";
thread.Start();
Thread.Sleep(20 00);
tester.Continue Running = false;
}
}
It now works without any volatile variables (but plenty of volatile
reads and writes!).
--
Jon Skeet - <skeet@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
Comment