4

I'm working in an API that calls the main function through multiple threads. I'm trying to access through that function a Bitmap in another class and write from it, but even after setting it to use completely different instance of the object I am experiencing an InvalidOperationException: Bitmap region is already locked.

I've tried locking code in the main function and where Bitmap.LockBits(...) is being called. Yes, UnlockBits is being called when I'm done.

    /* Part of Class B */
    public Surface imageSurface //Surface is a field of pixels, more or less.
    {
        get
        {
            if (_CurrImage != null && _imageSurface == null)
            {

                _imageSurface = Surface.CopyFromBitmap(_CurrImage);
                return Surface.CopyFromBitmap(_imageSurface.CreateAliasedBitmap());
            }
            else
            {
                Surface clearPixel = new Surface(1, 1);
                clearPixel[0, 0] = ColorBgra.FromBgra(255, 255, 255, 0);
                return clearPixel;
            }
        }
    }
    /* the "main" function, Class A */
    public override void Render(ClassBPrototype parameters, ...)
    {
        ClassB token = (ClassB)parameters; // Here we go again~!
        ...
        Surface ourSurface = dstArgs.Surface;
        if (token.imageSurface.Size != null)
        {
            ourSurface = token.imageSurface;
        }

        lock(typeof(ClassA))
        {
            for (int lRectangleIndex = ...)
            {
                Rectangle lRectangle = rois[lRectangleIndex];

                for (int y = ...)
                {
                    surfaceY = (ourSurface.Height / 2) - (y - (int)CenterY);

                    for (int x = ...)
                    {
                        surfaceX = (ourSurface.Width / 2) - (x - (int)CenterX);
                        if (surfaceX >= 0 && surfaceX < ourSurface.Width && surfaceY >= 0 && surfaceY < ourSurface.Height)
                        {
                            dstArgs.Surface[x, y] = ourSurface[surfaceX, surfaceY];
                        }
                        else
                        {
                            dstArgs.Surface[x, y] = ColorBgra.FromBgra(255, 255, 255, 0);
                        }

                    }
                }
            }
        }
    }
  • Please post a small, complete example that demonstrates the problem. – Andrew Hare Oct 12 '09 at 03:56
  • @Andrew: my thoughts exactly - the definition of "completely different instance" sounds key. – Marc Gravell Oct 12 '09 at 03:58
  • 6
    It may not be relate but it doesn't look right to me. You should not lock (typeof(ClassA)). I normally lock a private variable. See here for more details http://msdn.microsoft.com/en-us/library/c5kehkcz(v=vs.80).aspx – Harvey Kwok Dec 13 '10 at 06:17

1 Answers1

4

The problem is most likely that the shared resource - the Bitmap - is not protected by the lock in the code-example. The lock needs to wrap both the LockBits and UnlockBits calls to be effective against simultaneous access to the Bitmap instance.

In this case I would advice to use the actual Bitmap instance as lock object since this is what needs to be safeguarded agains simultaneous access from diffrent threads.

Locking on typeof(ClassA) is only recommended when using locks inside static methods.

Please note that locks are mutual exclusion locks and the other threads will be suspended while one single threads has aquired the lock. If most time is spent inside the lock no or little parallellization benefits will come from having multiple threads.

In case of an exception the lock will be released by design of the lock construct, while the UnlockBits method will typically not be called. If an exception can be thrown and catched I would recommend calling the UnlockBits method from a finally block. Something like this:

private Bitmap _bitmap;
public void foo()
{
    lock (_bitmap)
    {
        BitmapData data;
        try
        {
            data = _bitmap.LockBits(area, ImageLockMOde.ReadOnly, pixelFormat);
            UseBitmapData(data);
        }
        finally
        {
            _bitmap.UnlockBits(data);
        }
    }
}

This logic can also be wrapped in it's own class implementing the IDisposable interface allowing the powerful using construct to be used.

vidstige
  • 12,492
  • 9
  • 66
  • 110