Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object is currently in use elsewhere

Tags:

c#

locking

I'm getting this error, and it looks like it's because the same Bitmap object is being accessed by different threads. However I am using locks everywhere with it.

public class MySingleInstanceClass
{
    private Object locker = new Object();

    private Bitmap myImage = new Bitmap(100, 100);

    public Bitmap MyImage
    {
        get
        {
            lock (locker)
                return myImage;
        }
        private set
        {
            lock (locker)
                myImage = value;
        }
    }

    private void Refresh()
    {
        lock (locker)
        {
            var g = Graphics.FromImage(myImage);
            // do more processing
        }
    }
}

Class MySingleInstanceClass will have only one instance. Calls to MyImage and Refresh() may come from different threads. As far as I understand, the code inside lock(locker) will not be executed until it's finished in another thread, but I still get the error. Can anyone point a flaw in the code?

Exception looks like that:

A first chance exception of type 'System.InvalidOperationException' occurred in System.Drawing.dll

Error: Object is currently in use elsewhere.

at System.Drawing.Graphics.FromImage(Image image)

at (points to the line containing var g = Graphics.FromImage(myImage);)

like image 858
Georgii Oleinikov Avatar asked Feb 02 '13 10:02

Georgii Oleinikov


2 Answers

the locker object is not static; thus every new instance creates its own locker; you need to create locker as static in order to prevent access from other threads if using multiple objects.

private static Object locker = new Object();

For single object scenario, using a non static class level variable as a locker is proper. If you are employing this scenario I feel that the implementation of Singleton has some problems.

UPDATE:

public sealed class MySingleInstanceClass
{
    private static volatile MySingleInstanceClass instance;
    private static object syncRoot = new Object();
    private Bitmap myImage;

    private MySingleInstanceClass() 
    {
        myImage = new Bitmap(100, 100);
    }

    public static MySingleInstanceClass Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                        instance = new MySingleInstanceClass();
                }
            }

            return instance;
        }
    }  

    public Bitmap MyImage
    {
        get
        {
            lock (syncRoot)
                return myImage;
        }
        private set
        {
            lock (syncRoot)
                myImage = value;
        }
    }

    public void Refresh()
    {
        lock (syncRoot)
        {
            var g = Graphics.FromImage(myImage);
            // do more processing
        }
    }

}
like image 184
daryal Avatar answered Oct 12 '22 23:10

daryal


It doesn't matter whether the object that is locked is static or not. The problem is that the lock(locker) inside the getter method unlocks as soon as the bitmap is returned. The returned reference to the bitmap is not protected by the lock and can be modified at the same time as a call to Refresh occurs.

One possible solution would be to lock on the bitmap itself but that can introduce deadlocks if not done carefully.

like image 28
Dirk Avatar answered Oct 12 '22 22:10

Dirk