对象当前正在其他地方使用

本文关键字:其他 方使用 对象 | 更新日期: 2023-09-27 17:57:13

我收到此错误,看起来这是因为不同的线程正在访问相同的位图对象。但是,我在任何地方都使用锁。

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中发生了类型为"System.InvalidOperationException"的第一次机会异常.dll

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

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

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

对象当前正在其他地方使用

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

private static Object locker = new Object();

对于单个对象方案,使用非静态类级别变量作为储物柜是合适的。如果您使用这种情况,我觉得单例的实现存在一些问题。

更新:

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

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

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

在我的应用程序中,最好的解决方案是:

  • 将带有文件的目录复制到另一个 TMP。 目录(具有 GUID 名称)
  • 每个用户使用 TMP 文件
  • 删除包含文件的 TMP 目录

在我的应用程序中有:

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

复制几个文件添加大约 0,01-0,2 瑞典克朗对于每个请求,最好是所有应用程序和用户的静态锁定不要等待 10 分钟等待 raport 生成(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();
    }
您可以在将

映像发送到方法之前克隆该映像

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