[问题记录.dotnet]对象当前正在其他地方使用(Object is currently in use elsewhere) - Image.Save

今天有同事反馈了个问题。查看日志,是在通讯框架这层序列化报错,异常消息为“对象当前正在其他地方使用“。查看异常的堆栈,是Image.Save方法引发的。

经过简单排查,满足这些条件时会出现问题:1)返回对象中,Image类型字段的值非空且都是指向同一个Image实例;2)并发调用。


进行单独验证,测试代码如下:

[Serializable]
public class ImageTest
{
    System.Drawing.Image m_val;

    public System.Drawing.Image Val
    {
        get { return m_val; }
        set { m_val = value; }
    }
}
Image img = System.Drawing.Image.FromFile(@"D:\about.png");
Thread thread1 = new Thread(new ThreadStart(() =>
{
    byte[] bytes;
    try
    {
        for (int i = 0; i < 10; i++)
        {
            ImageTest obj = new ImageTest();
            obj.Val = img;
            using (MemoryStream stream = new MemoryStream())
            {
                new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize(stream, obj);
                bytes = stream.ToArray();
            }
        }
    }
    catch (Exception ex)
    {
        //...
    }
    Console.WriteLine("end.");
}));
Thread thread2 = new Thread(new ThreadStart(() =>
{
    byte[] bytes;
    try
    {
        for (int i = 0; i < 10; i++)
        {
            ImageTest obj = new ImageTest();
            obj.Val = img;
            using (MemoryStream stream = new MemoryStream())
            {
                new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize(stream, obj);
                bytes = stream.ToArray();
            }
        }
    }
    catch (Exception ex)
    {
        //...
    }
    Console.WriteLine("end.");
}));
thread1.Start();
thread2.Start();


异常:

Type:    System.InvalidOperationException
Message: 对象当前正在其他地方使用
          Object is currently in use elsewhere

StackTrace:
   在 System.Drawing.Image.get_RawFormat()
   在 System.Drawing.Image.Save(MemoryStream stream)
   在 System.Drawing.Image.System.Runtime.Serialization.ISerializable.GetObjectData(SerializationInfo si, StreamingContext context)
   在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
   在 System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
   在 System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)
   ...



原因:Image.Save 方法不是线程安全的(很多GDI+的方法都不是线程安全的)。同时、对同一个Image对象实例进行处理,就可能会导致线程异常。

建议:对返回值的类型做调整:如果Image字段必须要,改用byte[]类型。


备注:其他地方遇到这个错误,可能是加锁,以确保线程安全。


你可能感兴趣的:(DotNet)