对象目前在其他地方使用

时间:2013-02-02 10:12:12

标签: c# locking

我收到此错误,看起来是因为不同的线程正在访问相同的Bitmap对象。但是我随处可以使用锁。

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

MySingleInstanceClass只有一个实例。对MyImageRefresh()的调用可能来自不同的主题。据我所知,lock(locker)中的代码在另一个线程完成之前不会被执行,但我仍然得到错误。任何人都可以指出代码中的缺陷吗?

异常看起来像这样:

  

System.Drawing.dll中出现'System.InvalidOperationException'类型的第一次机会异常

     

错误:对象目前正在其他地方使用。

     

在System.Drawing.Graphics.FromImage(图片图片)

     

at(指向包含var g = Graphics.FromImage(myImage)的行;)

4 个答案:

答案 0 :(得分:9)

locker对象不是静态的;因此每个新实例都会创建自己的储物柜;您需要将locker创建为静态,以防止在使用多个对象时从其他线程进行访问。

private static Object locker = new Object();

对于单个对象场景,使用非静态类级别变量作为锁定器是正确的。如果您正在使用这种情况,我觉得Singleton的实现存在一些问题。

更新:

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

}

答案 1 :(得分:5)

锁定的对象是否是静态无关紧要。问题是,只要返回位图,getter方法中的lock(locker)就会解锁。返回的位图引用不受锁保护,可以在调用Refresh的同时进行修改。

一种可能的解决方案是锁定位图本身,但如果不仔细,可能会引入死锁。

答案 2 :(得分:1)

在我的应用中,最佳解决方案是:

  • 将dir与文件复制到另一个,tmp。目录(带有guid名称)
  • 每个用户使用tmp文件
  • 删除包含文件的tmp目录

在我的应用中有:

  • 每个请求长达1分钟
  • 最大用户数为120(内联网应用程序)
  • 没有人想要等待5-10分钟来生成raport

复制几个文件添加约0,01-0,2 sek。对于每个请求,最好是所有应用程序和用户的静态锁定不等待raport生成10分钟(10个用户在同一时刻点击生成按钮)。

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

答案 3 :(得分:0)

您可以在将图像发送到方法

之前克隆该图像
                Image newimg = (Image)img.Clone();