今天有同事反馈了个问题。查看日志,是在通讯框架这层序列化报错,异常消息为“对象当前正在其他地方使用“。查看异常的堆栈,是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[]类型。
备注:其他地方遇到这个错误,可能是加锁,以确保线程安全。