views:

3095

answers:

5

Hi, I am trying to lock a "boxed" object in a c# app, is this not possible?

    class t
    {
        System.Object t_x = new object();

        public t(int p)
        {
            t_x = p;

        }

        public void w()
        {
            lock (t_x)
            {
                for (int i = 0; i < 4; i++)
                {

                    {
                        t_x = ((int)t_x) + 1;
                        Console.WriteLine(t_x);
                        Thread.Sleep(1000);
                    }
                }
            }
        }
    }

In another class I can start 2 threads:

        Thread b1 = new Thread(new ThreadStart(t1.w));
        b1.Start();
        Thread b2 = new Thread(new ThreadStart(t1.w));
        b2.Start();

However the portion is not locked. When I lock an arbitrary object (i.e. one created and not modified as object a=new object()) it locks well. Is boxing operation somehow "depromotes" my Object??

+9  A: 

You need to create a separate lock object. The problem is that you re-assign t_x inside the loop. Assuming thread b1 gets inside the loop before b2 gets to the lock statement, b2 will be allowed inside the lock statement because, by that time, t_x will be a new object that does not have a lock on it.

AgileJon
I think Monitor it's a better way to do that
Cédric Boivin
@Cedric - read Lee's answer more carefully. The `lock` statement is exactly the same as using `Monitor.Enter` and `Monitor.Exit`. It's just a neater syntax for the same pattern. The issue is not whether to use `lock` or `Monitor` (not really a choice, as they are the same). The issue is to always pass the same object to serve as the lock, which is broken if you use a value type as it is boxed differently ever time you access it.
Daniel Earwicker
+10  A: 

No, you can't do this - the lock block is shorthand for the following:

try(Monitor.Enter(lockObject))
{
    //critical section
}
finally
{
    Monitor.Exit(lockObject)
}

The documentation for Monitor.Enter states, "Use Monitor to lock objects (that is, reference types), not value types. When you pass a value type variable to Enter, it is boxed as an object. If you pass the same variable to Enter again, it is boxed as a separate object, and the thread does not block"

Lee
That the good way to do it. It's in the first exam 70-536 of microsoft certification.
Cédric Boivin
This is the right answer - not because of the code snippet (the `lock` statement is fine) but because it mentions the difference between value and reference types. A value type is boxed again each time it is converted to the universal `object` base class, so it can't reliably act as the locking target. So this is better than the accepted answer.
Daniel Earwicker
+1  A: 

You have to use a an extra object for the lock

object lockObj = new object();
public void foo()
{
    lock(lockObj)
    {
    //do stuff here
    }
}
Femaref
+1  A: 

The lock (t_x) call boxes an integer as a temporary object. Each call to lock(t_x) creates a New object and locking is useless.

(Lock expects an object and creates a NEW temporary object from the integer)

Just create a seperate lock object like said above by Femaref.

Barfieldmv
A: 

If you really want (need?) to lock on the object, you can use a kind of wrapper :

public class IntWrapper
{
    public int Value{get;set;}
}

Or if you need to stay more abstract :

public class ObjectWrapper
{
    public Object Value { get;set; }
}
Toto