Georgii Oleinikov
Georgii Oleinikov

Reputation: 3915

Object is currently in use elsewhere

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);)

Upvotes: 5

Views: 21644

Answers (4)

zchpit
zchpit

Reputation: 3121

In my app the best solution was:

  • copy dir with files to another, tmp. catalog (with guid name)
  • use tmp files per user
  • delete tmp catalog with files in it

In my app there is:

  • each request is 1 min long
  • max users is 120 (intranet application)
  • nobody want to wait 5-10 min for raport to generate

Copy few files add about 0,01-0,2 sek. for each request, is better that static lock on all app and user don't neet to wait 10 min for raport to generate (10 users click generate button in same moment).

        private void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
    {
        // Get the subdirectories for the specified directory.
        DirectoryInfo dir = new DirectoryInfo(sourceDirName);
        DirectoryInfo[] dirs = dir.GetDirectories();

        if (!dir.Exists)
        {
            throw new DirectoryNotFoundException(
                "Source directory does not exist or could not be found: "
                + sourceDirName);
        }

        // If the destination directory doesn't exist, create it. 
        if (!Directory.Exists(destDirName))
        {
            Directory.CreateDirectory(destDirName);
        }

        // Get the files in the directory and copy them to the new location.
        FileInfo[] files = dir.GetFiles();
        foreach (FileInfo file in files)
        {
            string temppath = Path.Combine(destDirName, file.Name);
            file.CopyTo(temppath, false);
        }

        // If copying subdirectories, copy them and their contents to new location. 
        if (copySubDirs)
        {
            foreach (DirectoryInfo subdir in dirs)
            {
                string temppath = Path.Combine(destDirName, subdir.Name);
                DirectoryCopy(subdir.FullName, temppath, copySubDirs);
            }
        }
    }


        private void DeleteReportExecutionDirectory(string dirPath)
    {
        System.IO.DirectoryInfo downloadedMessageInfo = new DirectoryInfo(dirPath);
        foreach (FileInfo file in downloadedMessageInfo.GetFiles())
        {
            file.Delete();
        }
        foreach (DirectoryInfo dir in downloadedMessageInfo.GetDirectories())
        {
            dir.Delete(true);
        }
        downloadedMessageInfo.Delete();
    }

Upvotes: 1

user3290286
user3290286

Reputation: 717

you can clone that image before send it to method

                Image newimg = (Image)img.Clone();

Upvotes: 0

daryal
daryal

Reputation: 14929

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
        }
    }

}

Upvotes: 11

Dirk
Dirk

Reputation: 10968

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.

Upvotes: 5

Related Questions