473,396 Members | 1,755 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

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

Thread safety of readonly members

Hi, I can't find a statement about this in the threading
sections in the doc...

Consider:

class Class1
{
Class1()
{
_val = 42;
}

public int getVal()
{
return _val;
}

private readonly int _val;
}

In a threaded environment, is it necessary to interlock
inside the constructor and getVal() to ensure that
threads get the correct value? In other words,
do I have to write it as follows?

class Class1
{
Class1()
{
lock(this)
{
_val = 42;
}
}

public int getVal()
{
lock(this)
{
return _val;
}
}

private readonly int _val;
}

In C++, the lock is necessary because, on SMP
machines, memory consistency isn't guaranteed without
the lock. But I don't know whether the same is true
for C#.

What if the member variable is not readonly (but will
never be modified, except for the initial assignment
in the constructor)? Is accessing the value thread-safe
without a lock in that case?

What about const members? Is access to those safe without
a lock?

And what about static members that are (conceptually)
immutable? Is the lock required there?

Thanks muchly,

Michi.
Nov 15 '05 #1
14 1818
"Michi Henning" <mi***@zeroc.com> wrote in message
news:eq**************@TK2MSFTNGP12.phx.gbl...
In a threaded environment, is it necessary to interlock
inside the constructor and getVal() to ensure that
threads get the correct value? In other words,
do I have to write it as follows?
In the specific example you gave, it is not necessary, for a few reasons.

Since all you have is the constructor and getVal(), there is never any
competition for the shared resource. When the object is being constructed,
nobody has a reference to it yet, so nobody can call getVal(). You can be
sure that anybody calling getVal() will be calling it after the constructor
has finished executing.

The data can never be in an inconsistent state since it is just a single
int. Ints are assigned atomically. I'm not sure about other types - I am
sure that structs are not assigned atomically, but I'm not positive about
numbers that are > 32 bits (I suspect they are not atomic - perhaps someone
can clear this up). Suppose we add a putVal() method to complicate things.
Suppose that someone calls putVal(0) around the same time someone calls
getVal(). What will they get? Well, they will either get 42 or 0. A lock
doesn't change that.
In C++, the lock is necessary because, on SMP
machines, memory consistency isn't guaranteed without
the lock. But I don't know whether the same is true
for C#.
In the cases where this is true of C++, it is also true of C#. Your
specific example doesn't require synchronization in either language, but I
realize you were probably just trying to simplify things for your post.
What if the member variable is not readonly (but will
never be modified, except for the initial assignment
in the constructor)? Is accessing the value thread-safe
without a lock in that case?
Yes.
What about const members? Is access to those safe without
a lock?
Yes.
And what about static members that are (conceptually)
immutable? Is the lock required there?


No lock required in this case either.

I'm not sure from your post if you have a basic understanding of
multithreaded programming. The principles are the same in most languages.
If you are unsure, I'd suggest doing some reading on that topic.

HTH
Mike
Nov 15 '05 #2
Re:; the atmomicity of types. Section 5.5 of the spec reads:

5.5 Atomicity of variable references

Reads and writes of the following data types are atomic: bool, char, byte,
sbyte, short, ushort, uint, int, float, and reference types. In addition,
reads and writes of enum types with an underlying type in the previous list
are also atomic. Reads and writes of other types, including long, ulong,
double, and decimal, as well as user-defined types, are not guaranteed to be
atomic. Aside from the library functions designed for that purpose, there is
no guarantee of atomic read-modify-write, such as in the case of increment
or decrement.

Richard
--
C#, .NET and Complex Adaptive Systems:
http://blogs.geekdojo.net/Richard
"Michael Sparks" <mi************@remove.this.sbcglobal.net> wrote in message
news:yB*****************@newssvr23.news.prodigy.co m...
"Michi Henning" <mi***@zeroc.com> wrote in message
news:eq**************@TK2MSFTNGP12.phx.gbl...
In a threaded environment, is it necessary to interlock
inside the constructor and getVal() to ensure that
threads get the correct value? In other words,
do I have to write it as follows?
In the specific example you gave, it is not necessary, for a few reasons.

Since all you have is the constructor and getVal(), there is never any
competition for the shared resource. When the object is being

constructed, nobody has a reference to it yet, so nobody can call getVal(). You can be
sure that anybody calling getVal() will be calling it after the constructor has finished executing.

The data can never be in an inconsistent state since it is just a single
int. Ints are assigned atomically. I'm not sure about other types - I am
sure that structs are not assigned atomically, but I'm not positive about
numbers that are > 32 bits (I suspect they are not atomic - perhaps someone can clear this up). Suppose we add a putVal() method to complicate things. Suppose that someone calls putVal(0) around the same time someone calls
getVal(). What will they get? Well, they will either get 42 or 0. A lock doesn't change that.
In C++, the lock is necessary because, on SMP
machines, memory consistency isn't guaranteed without
the lock. But I don't know whether the same is true
for C#.


In the cases where this is true of C++, it is also true of C#. Your
specific example doesn't require synchronization in either language, but I
realize you were probably just trying to simplify things for your post.
What if the member variable is not readonly (but will
never be modified, except for the initial assignment
in the constructor)? Is accessing the value thread-safe
without a lock in that case?


Yes.
What about const members? Is access to those safe without
a lock?


Yes.
And what about static members that are (conceptually)
immutable? Is the lock required there?


No lock required in this case either.

I'm not sure from your post if you have a basic understanding of
multithreaded programming. The principles are the same in most languages.
If you are unsure, I'd suggest doing some reading on that topic.

HTH
Mike

Nov 15 '05 #3
Michael Sparks <mi************@remove.this.sbcglobal.net> wrote:
In the specific example you gave, it is not necessary, for a few reasons.

Since all you have is the constructor and getVal(), there is never any
competition for the shared resource. When the object is being constructed,
nobody has a reference to it yet, so nobody can call getVal(). You can be
sure that anybody calling getVal() will be calling it after the constructor
has finished executing.


That's not quite true, actually. Unfortunately, compiler memory
reordering could end up with the pseudocode of:

Thread 1:
o Create new "empty" object
o Store reference
o Call constructor

A second thread could come in between the second and third parts, and
call getVal on the reference.

Basically, unless you've got memory fences involved, I believe the
compiler is free to do whatever it likes in terms of reordering, so
long as each individual thread sees something consistent with itself.

It's only theoretical, and I strongly suspect that it would never
happen on any current CLR implementation, but I believe it *is* a
potential problem. Effectively, the issue is how the reference itself
is visible - if the reference is assigned in a lock (and the same lock
is required to access it for the first time), all would be well.

Of course, I could be entirely wrong about this, but that's the way I
understand it...

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Nov 15 '05 #4
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MP************************@msnews.microsoft.c om...
Thread 1:
o Create new "empty" object
o Store reference
o Call constructor

A second thread could come in between the second and third parts, and
call getVal on the reference.

Basically, unless you've got memory fences involved, I believe the
compiler is free to do whatever it likes in terms of reordering, so
long as each individual thread sees something consistent with itself.


Aaah. I was making an assumption based on the newobj IL opcode, which
should behave (I think) as I stated. I didn't think about the JIT
reordering things.
Learn something new every day. :-)

Mike
Nov 15 '05 #5
"Michael Sparks" <mi************@remove.this.sbcglobal.net> wrote in message
news:yB*****************@newssvr23.news.prodigy.co m...
"Michi Henning" <mi***@zeroc.com> wrote in message
news:eq**************@TK2MSFTNGP12.phx.gbl...
In a threaded environment, is it necessary to interlock
inside the constructor and getVal() to ensure that
threads get the correct value? In other words,
do I have to write it as follows?
In the specific example you gave, it is not necessary, for a few reasons.

Since all you have is the constructor and getVal(), there is never any
competition for the shared resource. When the object is being constructed,
nobody has a reference to it yet, so nobody can call getVal(). You can be
sure that anybody calling getVal() will be calling it after the constructor
has finished executing.


That's not the problem. The question is whether it is possible for another
thread to read a stale value of the memory location that holds _val some
time after the constructor completes. On SMP machines, if no memory
barrier is inserted after the initialization, another thread can read a stale
value out of its cache. So, I guess another way to formulate my questions
is to ask whether C# sharp guarantees to insert memory barriers after
initializating readonly data members.
The data can never be in an inconsistent state since it is just a single
int. Ints are assigned atomically.
Again, that's not relevant. The issues isn't whether the value is assigned
to atomically, but whether the lock is required to guarantee that
memory is consistent across different threads after the initialization.
Suppose we add a putVal() method to complicate things.
Suppose that someone calls putVal(0) around the same time someone calls
getVal(). What will they get? Well, they will either get 42 or 0. A lock
doesn't change that.
Uh, no -- if there is no memory barrier, another thread might read 99,
if that is the value that was in the corresponding memory location
last time that thread read it.
In C++, the lock is necessary because, on SMP
machines, memory consistency isn't guaranteed without
the lock. But I don't know whether the same is true
for C#.


In the cases where this is true of C++, it is also true of C#. Your
specific example doesn't require synchronization in either language, but I
realize you were probably just trying to simplify things for your post.


Sorry, no -- in C++, you definitely need the lock. That's because
C++ doesn't make any promises as to memory consistency in the
absence of locks. I've seen the equivalent C++ actually blow up
on Alpha SMP machines, for example.
I'm not sure from your post if you have a basic understanding of
multithreaded programming. The principles are the same in most languages.
If you are unsure, I'd suggest doing some reading on that topic.


Uh, as one of the implementers of POSIX Draft 4 UNIX kernel
threads back in 1990, I think I have a passing familiarity with
the topic ;-)

Cheers,

Michi.

--
Michi Henning Ph: +61 4 1118-2700
ZeroC, Inc. http://www.zeroc.com,
http://www.triodia.com/staff/michi

Nov 15 '05 #6
"Richard A. Lowe" <ch*****@yumspamyumYahoo.com> wrote in message
news:eD**************@TK2MSFTNGP11.phx.gbl...
Re:; the atmomicity of types. Section 5.5 of the spec reads:

5.5 Atomicity of variable references

Reads and writes of the following data types are atomic: bool, char, byte,
sbyte, short, ushort, uint, int, float, and reference types. In addition,
reads and writes of enum types with an underlying type in the previous list
are also atomic. Reads and writes of other types, including long, ulong,
double, and decimal, as well as user-defined types, are not guaranteed to be
atomic. Aside from the library functions designed for that purpose, there is
no guarantee of atomic read-modify-write, such as in the case of increment
or decrement.


Right, but this doesn't apply to my question, which really is about memory
consistency, not atomicity.

But that raises an interesting question: why bother providing the atomicity
guarantee in the language at all? The guarantee would appear to be useless
in the absence of locks. But, in the presence of locks, the guarantee would
appear to be unnecessary?

Cheers,

Michi.
--
Michi Henning Ph: +61 4 1118-2700
ZeroC, Inc. http://www.zeroc.com

Nov 15 '05 #7
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MP************************@msnews.microsoft.c om...

That's not quite true, actually. Unfortunately, compiler memory
reordering could end up with the pseudocode of:

Thread 1:
o Create new "empty" object
o Store reference
o Call constructor

A second thread could come in between the second and third parts, and
call getVal on the reference.

Basically, unless you've got memory fences involved, I believe the
compiler is free to do whatever it likes in terms of reordering, so
long as each individual thread sees something consistent with itself.
Right -- that's exactly what I was getting at. So, no guarantees in
the language then about readonly member initialization. Thanks
for clarifying this Jon!
It's only theoretical, and I strongly suspect that it would never
happen on any current CLR implementation, but I believe it *is* a
potential problem. Effectively, the issue is how the reference itself
is visible - if the reference is assigned in a lock (and the same lock
is required to access it for the first time), all would be well.
Right, it wouldn't happen on an Intel processor. But
I know that the race is real on some architectures. For example,
double-checked locking blows up on Alpha SMP machines because
the memory model is more liberal than that of an Intel processor.
So, the code I showed could break if run on such a machine,
for the same reason.

Cheers,

Michi.

--
Michi Henning Ph: +61 4 1118-2700
ZeroC, Inc. http://www.zeroc.com

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


Nov 15 '05 #8
Hello
It's only theoretical, and I strongly suspect that it would never
happen on any current CLR implementation, but I believe it *is* a
potential problem. Effectively, the issue is how the reference itself
is visible - if the reference is assigned in a lock (and the same lock
is required to access it for the first time), all would be well.


I don't know if such a problem exists. Personally, I think it would be a CLR
bug if such a problem exist in any CLR implementation, because this would
allow access to an object without the constructor being called, which
destroys the concept of having a contructor. This can cause a lot of bugs
and security holes in programs. Also, the performance loss caused by the
need to make a lock on constructor calls, would be more than the performance
gain by such reordering.

Best regards,
Sherif
Nov 15 '05 #9
"Sherif ElMetainy" <el*************@wayout.net.NOSPAM> wrote in message
news:%2***************@TK2MSFTNGP12.phx.gbl...
Hello
It's only theoretical, and I strongly suspect that it would never
happen on any current CLR implementation, but I believe it *is* a
potential problem. Effectively, the issue is how the reference itself
is visible - if the reference is assigned in a lock (and the same lock
is required to access it for the first time), all would be well.


I don't know if such a problem exists. Personally, I think it would be a CLR
bug if such a problem exist in any CLR implementation, because this would
allow access to an object without the constructor being called, which
destroys the concept of having a contructor.


No, I don't think that can happen. Some time after a thread has
allocated the object, it has to pass the reference to another thread
somehow. Of course, that has to happen either as a method parameter
(in which case there is no locking issue), or the reference is passed
by assigning it to some variable that is read by another thread. In the
latter case, the thread that writes the reference must of course interlock
with the thread that reads the reference.

The point of the example I showed is not about passing the reference
in a thread-safe way, but about accessing the contents of the object
pointed at by the reference. From the discussion, it appears that,
without acquiring a lock in the constructor and in getVal(), there
is no guarantee that another thread will read the correct value,
not withstanding the fact that the constructor has finished executing.

I got a reference to the following in the Mono development list,
which explains it nicely (and confirms that the locks are required):

http://research.microsoft.com/~birre...eadsCSharp.pdf

Cheers,

Michi.

--
Michi Henning Ph: +61 4 1118-2700
ZeroC, Inc. http://www.zeroc.com

Nov 15 '05 #10
"Michi Henning" <mi***@triodia.com> wrote in message
news:%2****************@TK2MSFTNGP12.phx.gbl...
That's not the problem. The question is whether it is possible for another
thread to read a stale value of the memory location that holds _val some
time after the constructor completes. On SMP machines, if no memory
barrier is inserted after the initialization, another thread can read a stale value out of its cache. So, I guess another way to formulate my questions
is to ask whether C# sharp guarantees to insert memory barriers after
initializating readonly data members.
Sorry, I thought you were trying to use mutual exclusion to maintain an
invariant on the int, which of course isn't necessary.
Uh, as one of the implementers of POSIX Draft 4 UNIX kernel
threads back in 1990, I think I have a passing familiarity with
the topic ;-)


My apologies if I have insulted your manhood. :-)

Mike
Nov 15 '05 #11
Michael Sparks wrote:
Uh, as one of the implementers of POSIX Draft 4 UNIX kernel
threads back in 1990, I think I have a passing familiarity with
the topic ;-)

My apologies if I have insulted your manhood. :-)


No, no insult taken :-)

Cheers,

Michi.
Nov 15 '05 #12
Hello Michi
http://research.microsoft.com/~birre...eadsCSharp.pdf

Thanks for the link for the document, it is great. One issue the author
discusses in section 4.3, here is his sample code

Foo theFoo = null;
public Foo GetTheFoo() {
if (theFoo == null) {
lock (this) {
if (theFoo == null)
theFoo = new Foo();
}
}
return theFoo;
}
The author says the above code may cause race conditions. I quote the Author
now
Start Quote "

First, if "GetTheFoo" is called on processor A and then later on processor
B, it's possible that processor B will see the correct non-null object
reference for "theFoo", but will read incorrectly cached values of the
instance variables inside "theFoo", because they arrived in B's cache at
some earlier time, being in the same cache line as some other variable that'
s cached on B. *
Second, it's legitimate for the compiler to re-order statements within a
"lock" statement, if the compiler can prove that they don't interfere.
Consider what might happen if the compiler makes the initialization code for
"new Foo()" be inline, and then re-orders things so that the assignment to
"theFoo" happens before the initialization of the instance variable's of
"theFoo". A thread running concurrently on another processor might then see
a non-null "theFoo" before the object instance is properly initialized.
"
End Quote

I don't quite understand the first point, I understand the second issue,
which is similar to what Jon has mentioned. But a look at the source code of
some of the .NET classes shows that Microsoft makes this mistake (if this is
a mistake) at many occasions

for example below is the code of a method called InitResourceManager in
System.Environment class
private static ResourceManager InitResourceManager() {
if (SystemResMgr == null) {
lock(typeof(Environment)) {
if (SystemResMgr == null) {
// Do not reorder these two field assignments.
m_resMgrLockObject = new Object();
SystemResMgr = new ResourceManager("mscorlib",
typeof(String).Assembly);
}
}
}
return SystemResMgr;
}
The above code is basically the same as the document's author faulty code,
yet it is extracted from the .NET (Rotor) source code.
So is there anywhere in .NET's documentation and specifications something
that mentions whether or not it is gauranteed that an object's constructor
must be called before any thread is allowed to access it? Or this assignment
/ constructor reordering? This is a very important question for me, because
I have been following Microsoft's example above in a lot my applications,
and it would be a problem for me if this doesn't work.

Best regards,
Sherif
Nov 15 '05 #13
Sherif ElMetainy <el*************@wayout.net.NOSPAM> wrote:

<snip>
The above code is basically the same as the document's author faulty code,
yet it is extracted from the .NET (Rotor) source code.
So is there anywhere in .NET's documentation and specifications something
that mentions whether or not it is gauranteed that an object's constructor
must be called before any thread is allowed to access it? Or this assignment
/ constructor reordering? This is a very important question for me, because
I have been following Microsoft's example above in a lot my applications,
and it would be a problem for me if this doesn't work.


It *might* work, but it's not good to rely on it, because it's not
guaranteed to work. The thing to read is the section on memory
guarantees in the CLR spec.

Of course, the Rotor project may decide to use technically dodgy code
in its own BCL if it absolutely knows that its own VES *doesn't* make
it problematic. Still not a good idea though, IMO...

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Nov 15 '05 #14
"Sherif ElMetainy" <el*************@wayout.net.NOSPAM> wrote in message
news:O3**************@TK2MSFTNGP11.phx.gbl...
I don't quite understand the first point, I understand the second issue,
which is similar to what Jon has mentioned.
The first issue is about how CPU memory caches work. If a thread
reads, for example, a four-byte integer at address 0x10 from
physical memory, the underlying hardware will read a cache
line. Cache lines typically contain several words of memory (16 or 32
bytes or more). So, the access of four bytes at 0x10 causes more
memory to be cached by the CPU, for example, all the memory
contents from 0x10 to 0x2F (assuming that a cache line is 32 bytes
long).

So, assume that thread 1 at some time in the past has read a variable
at address 0x10. That means that the contents of 0x10-0x2F were read into
the CPU cache of thread 1 at that time. Some time later, thread 2 runs
on a different CPU and allocates the Class1 instance. It just so happens
that the instance ends up at address 0x14. Thread 2 also runs the
constructor of the instance.

Some time later, thread 1 calls getVal() on that instance. The CPU of
thread 1 realizes that it has the contents of address 0x14 already in its cache
and
satisfies the read from the cache, not knowing that the contents of
that memory location were previously modified by thread 2, so thread 1
reads a garbage value. With the locks in place, this can't happen because
that causes the compiler to insert instructions that ensure memory
consistency.
The above code is basically the same as the document's author faulty code,
yet it is extracted from the .NET (Rotor) source code.


On an x86 machine, double-checked locking happens to work. But that's
not true for other architectures. In particular, double-checked locking fails
on Alphas. So, the code is non-portable.

Cheers,

Michi.

--
Michi Henning Ph: +61 4 1118-2700
ZeroC, Inc. http://www.zeroc.com

Nov 15 '05 #15

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

Similar topics

1
by: Frank Rizzo | last post by:
Some of the classes in the framework are marked as thread-safe in the documentation. In particular the docs say the following: "Any public static (*Shared* in Visual Basic) members of this type...
4
by: The Crow | last post by:
for example i have static readonly SqlParameter and i want to clone them at runtime. as clone operation will not write to SqlParameter object, just reading, should i lock that object during read...
11
by: dee | last post by:
OleDbCommand class like many .NET classes has the following description in its help file: "Thread Safety Any public static (Shared in Visual Basic) members of this type are safe for...
4
by: | last post by:
I find in the documentation the following Thread Safety Any public static (Shared in Visual Basic) members of this type are safe for multithreaded operations. Any instance members are not...
3
by: Nice | last post by:
how to comprehend the "Thread Safety"?
4
by: Warren Sirota | last post by:
Hi, I've got a method that I want to execute in a multithreaded environment (it's a specialized spider. I want to run a whole bunch of copies at low priority as a service). It works well running...
6
by: fniles | last post by:
I am using VB.NET 2003 and a socket control to receive and sending data to clients. As I receive data in 1 thread, I put it into an arraylist, and then I remove the data from arraylist and send it...
10
by: Paul | last post by:
Hi all, All of the classes in my DAL are static, with constants defining the stored procedures and parameters. I've been having some problems with my site which makes me wonder if there's a...
6
by: Olumide | last post by:
Hi - I've got a class that contains static member functions alone, all of whose arguments are passed by reference as shown below: class MySpiffyClass{ // no constructor, destructor or...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
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...
0
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,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
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...
0
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,...

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.