how to prohibit writing to a collection by other threads ?

B

bonk

Does anyone have a simple example on how to prohibit that any thread
other than the current thread modifies a certain object (a collection)
while we are in a certain section of the code?
In other words: while we are inside this codeblock whoever might think
of modified that particular collection has to wait until we have left
that codeblock.

As far as I understand it lock() {} only prohibits that other threads
enter a certain block of code while the current thread is inside of it
but does not prohibit modification of the object that was passed as the
parameter for lock() ...
 
A

Andy

I belive this is why your collection instance has a SyncRoot property;
all your threads should lock on that object before attempting to modify
the contents of the collection.

HTH
Andy
 
B

Brian Gideon

bonk said:
Does anyone have a simple example on how to prohibit that any thread
other than the current thread modifies a certain object (a collection)
while we are in a certain section of the code?
In other words: while we are inside this codeblock whoever might think
of modified that particular collection has to wait until we have left
that codeblock.

As far as I understand it lock() {} only prohibits that other threads
enter a certain block of code while the current thread is inside of it
but does not prohibit modification of the object that was passed as the
parameter for lock() ...

That's correct. The lock keyword will not prevent modification or even
access to the object used in that statement. What it prevents is a
thread from entering a critical section (that's the code block wrapped
by the lock) if another thread has already entered a critical section
guarded by the same object. So what you need to do is make sure that
all modifications to your collection are guarded by the same object.
If you're using a collection from the System.Collection namespace then
the SyncRoot property of the collection is the ideal candidate for the
guard object, but depending on exactly what you're after and what
specific collection you're using you may choose a different object.

Brian
 
B

bonk

That's correct. The lock keyword will not prevent modification or even
access to the object used in that statement. What it prevents is a
thread from entering a critical section (that's the code block wrapped
by the lock) if another thread has already entered a critical section
guarded by the same object.

But what is the Paramter for the lock() statement for then? To prohibit
other threads form entering a certain codeblock I wouldn't need some
strange other object. What does it mean to "obtain at mutual-exclusion
lock for a given object"? How does doing a lock(this){} differ from a
lock(someotherRefType){} ?
So what you need to do is make sure that
all modifications to your collection are guarded by the same object.
If you're using a collection from the System.Collection namespace then
the SyncRoot property of the collection is the ideal candidate for the
guard object, but depending on exactly what you're after and what
specific collection you're using you may choose a different object.

The situation is this: A method does a lengthy operation on the
collection (iterating over it an modifiing it and its content) while
this method is running no other object that might have a refernce to
that collection and no other thread else is allowed to modify that
collection.
 
A

Andy

bonk said:
But what is the Paramter for the lock() statement for then? To prohibit
other threads form entering a certain codeblock I wouldn't need some
strange other object. What does it mean to "obtain at mutual-exclusion
lock for a given object"? How does doing a lock(this){} differ from a
lock(someotherRefType){} ?

easy; any thread that needs your collection (even if they are only
iterating it) needs to do this:

// myCollection is a
System.Collections.Generic.List<System.Drawing.Point>, just for
example..
lock( myCollection.SyncRoot ) {
// Do list stuff here
}

That will keep your access to the collection thread safe.
 
B

Brian Gideon

bonk said:
But what is the Paramter for the lock() statement for then? To prohibit
other threads form entering a certain codeblock I wouldn't need some
strange other object.

Every object can have a monitor associated with it. The lock statement
automatically generates the calls to Monitor.Enter and Monitor.Exit at
compile time. When Monitor.Enter is called the monitor associated with
the object parameter is acquired if it's not currently "held" or the
call blocks until it can be acquired. When Monitor.Exit is called the
monitor is released allowing other threads the chance to acquire it.
So the lock isn't protecting the object parameter at all. Instead, the
object parameter is the how Monitor class uniquely identifies a
monitor.
What does it mean to "obtain at mutual-exclusion lock for a given
object"?

I see how that statement is confusing. It does not mean that access to
the object itself is constrained or "locked". It just means that the
object is used to constrain or "lock" execution of a code block. The
object is only used to help identify the lock.
How does doing a lock(this){} differ from a
lock(someotherRefType){} ?

They're using different objects so they would be associated with
different monitors. In other words, the locks are unrelated and
operate independently of each other.
The situation is this: A method does a lengthy operation on the
collection (iterating over it an modifiing it and its content) while
this method is running no other object that might have a refernce to
that collection and no other thread else is allowed to modify that
collection.

Anytime you access the collection make sure it is guarded by locks
using the *same* object. In the case of an ICollection the SyncRoot
property provides a good choice, but you're free to use another object
and it would still be safe.
 
B

bonk

Consider the following scenario: I am writing an API for others to use. It
has some public classes that expose some some fields as public (via property
acessors) or as protected (as fields directly) and they therefore can be
freely acessed by the user of my class (either by instanciating or deriving
from that class). Now inside some internal methods of these public classes
of my API I have to work with those fields using threads over a longer time.
So my goal is now to prohibt access to those public or protected fields
while one of my internal threads is doing some work with them. The problem
here:

The users of my class can not and should not have to know that they have to
aquire a lock to some sort of objects if they want to read or write from/to
those fields. I would like to abstact that away from the API view. Rather I
would like my API users automatically (whether they spawned their own thread
or wether they are on the main thread) to be forced to wait until my thread
is done and access is allowed again.

Hope that makes sense ...
 
B

Brian Gideon

Yes, that makes more sense. Here's the deal and a few things to
consider...

Usually you want to avoid making instance members of your public
classes thread-safe. That's because some users of your library may not
be using it in a multithreaded environment and those that do will have
to enforce their on synchronization on your classes. That makes your
job easier. That's how most of the .NET base class library is
designed.

Of course that doesn't help with your library's own internal
thread-safety requirements. And you're right. Users of your library
shouldn't have to worry or even know that it is using other threads
behind the scenes whether or not you decide to make the public
interface thread-safe or not.

One strategy for dealing with this is to make your objects immutable.
The only way your threads can modify data is if they create new copies
of it. Once they have completed whatever lengthy calculation was
required to create the new data the public references would be swapped
out (in a thread-safe manner of course). Do you see how that
eliminates a lot of the complexity? The string data type in .NET,
although a reference type, is immutable. That's why it is inherently
safe to access and modify it from multiple threads.

Another strategy that might work for you is to copy any data structures
the threads will use before you kick them off. That way the threads
will be working with private copies while the original public copies
are still valid. After the threads finish their work swap the public
references out (again, in a thread-safe manner) to publish the new
data.

Both approaches have a similar theme. That is they both work by making
sure the public data structures and the data structures the threads are
working with are separate. That requires less synchronization which
makes things easier for you. They both have a similar caveat though.
How do callers of your library know when the data structures have been
updated? How much extra work is required to repeatedly make separate
copies and publish the updates? Would these strategies have unexpected
side effects to the callers of your library? You'll need to have
answers to those questions.

public class Example
{
private SomeObject foo = new SomeObject();
private Object lockObject = new Object();

public SomeObject Foo
{
get
{
lock (lockObject)
{
return foo;
}
}
}

// This method might be invoked by another thread.
private void ChangesFooAsynchronously()
{
// Copy the data structure.
SomeObject copy;
lock (lockObject)
{
// This must be a deep copy.
copy = foo.Clone();
}

// We can safely access the copied data structure here.
copy.ChangeMe();
copy.ChangeMeAgain();

// Now publish the result.
lock (lockObject)
{
foo = copy;
}
}

}

Brian
 
B

bonk

Brian, thank you for your detailed answers. You have been great help in
understanding all this. It all makes perfect sense now. And I know
where to go from here.
 
B

Brian Gideon

Brian said:
public class Example
{
private SomeObject foo = new SomeObject();
private Object lockObject = new Object();

public SomeObject Foo
{
get
{
lock (lockObject)
{
return foo;
}
}
}

// This method might be invoked by another thread.
private void ChangesFooAsynchronously()
{
// Copy the data structure.
SomeObject copy;
lock (lockObject)
{
// This must be a deep copy.
copy = foo.Clone();
}

// We can safely access the copied data structure here.
copy.ChangeMe();
copy.ChangeMeAgain();

// Now publish the result.
lock (lockObject)
{
foo = copy;
}
}

}

There is an error in my example. As written it wouldn't be safe at
all. It should have looked like this. The copy must be done on the
caller's thread.

public class Example
{
private SomeObject foo = new SomeObject();
private SomeObject fooCopy = null;
private Object lockObject = new Object();

public SomeObject Foo
{
get
{
lock (lockObject)
{
return foo;
}
}
}

public void DoSomethingThatStartsThread()
{
// Copy the data structure.
lock (lockObject)
{
// This must be a deep copy.
fooCopy = foo.Clone();
}

// Start ChangeFooAsynchronously in another thread.
}

private void ChangeFooAsynchronously()
{
// We can safely access the copied data structure here.
fooCopy.ChangeMe();
fooCopy.ChangeMeAgain();

// Now publish the result.
lock (lockObject)
{
foo = fooCopy;
}
}

}
 

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