Friday, January 25, 2008

Locking Objects And Base Types

If you have a class that can be accessed with multiple threads you will need to wrap any outside access to private level variables with a lock statement. See Figure 1.0 where we have a customer class and a process class with a customer inside.

Figure 1.0

public class Customer
{
private int _customerId;
private string _customerName;

public int CustomerId
{
get {return _customerId; }
set {_customerId = value; }
}
}

public class Process
{
private IList _customersToProcess;

public IList CustomersToProcess
{
get
{
lock(_customersToProcess)
{
return _customersToProcess;
}
}
set
{
lock(_customersToProcess)
{
_customersToProcess = value;
}
}
}
}


The lock statement only takes objects. In order to lock base types, you must declare them as objects and then initialize them with their base type. When you do this, the object will default similar to what happens with VB.NET. Booleans will default to false, and integers to 0, and DateTime to MinValue. Strings can be locked without using boxing and unboxing from an object type since they are already objects. When in the getter the object must be cast back into the base type. Take a look at the Figure 1.1 below.

Figure 1.1

public class Customer
{
private int _customerId;
private string _customerName;

public int CustomerId
{
get {return _customerId; }
set {_customerId = value; }
}
}

public class Process
{
private object _numberOfThreads= new int();
private IList _customersToProcess;

public int NumberOfThreads
{
get
{
lock (_numberOfThreads)
{
return (int) _numberOfThreads;
}
}
set
{
_numberOfThreads= value;
}
}

public IList CustomersToProcess
{
get
{
lock(_customersToProcess)
{
return _customersToProcess;
}
}
set
{
lock(_customersToProcess)
{
_customersToProcess = value;
}
}
}
}


The example above is for illustrative purposes only. In a later post we will talk about using events to do custom progress information.

4 comments:

Matt Casto said...

The lock statement doesn't just take objects; it also takes types. A very common method is to just lock "this".

You can also create an object that is used only for locking, which is useful as well if you want to lock on more than one object in a single class or throughout multiple classes. But, really, how often does this come up?

Also, you might want to take a look at the volatile keyword for accessing values inside of thread.

Greg Finzer said...

What would be the syntax for locking an integer then? When compiling using the lock on an integer, I get the error "int is not a reference type as required by the lock statement"

Jim Holmes said...

Never, EVER lock "this." Bad juju. See Phil Haack's article at http://haacked.com/archive/2005/04/12/neverlockthis.aspx.

Sean Bibbey said...

NEVER lock strings. Strings are interned, i.e. string a = "abcd"; string b = "abcd"; are the same underlying object. So by locking string a, in effect string b is also locked.

Interned strings span application domains, so the havoc one can do by locking strings can be quite extensive.

Check out this article for more locking gotchas to watch out for.

http://www.deez.info/sengelha/2006/05/25/csharp-locking-rules