问题描述
我的印象是 lock() 会阻止多个线程同时访问一个对象.
I was under the impression that lock() would prevent multiple threads from accessing an object simultaneously.
但是,以下代码仍然经常抛出 InvalidOperationException(对象当前正在其他地方使用):
But, an InvalidOperationException (Object is currently in use elsewhere) is still frequently thrown by the following code:
lock (this) { localCopy = (Bitmap)this.bm.Clone(); int x, y; float pX = this.p.x; int width = localCopy.Width; x = (int)Math.Round((double)(pX * (float)width)); if (x >= localCopy.Width) x = localCopy.Width - 1; y = (int)Math.Round((double)(this.p.y * (float)localCopy.Height)); if (y >= localCopy.Height) y = localCopy.Height - 1; colourPixel = localCopy.GetPixel(x, y); }
注意事项:
- 我拆分了x的计算来隔离异常的原因.它似乎来自访问位图.
- 我尝试创建位图的本地副本,但这只会导致相同的异常.我试过 Clone()ing 并创建一个新的位图.两者都不起作用.
- 我尝试锁定 this(如所见)和位图对象.两者都不起作用.
- I split up the calculation of x to isolate the cause of the exception. It appears to come from accessing the bitmap.
- I tried creating a local copy of the bitmap, but this just causes the same exception. I've tried Clone()ing and creating a new Bitmap. Neither works.
- I've tried locking on this (as seen) and on the bitmap object. Neither works.
我是否试图以一种不应该的方式使用 lock()?我误解了它的目的吗?如何防止 InvalidOperationExceptions?
Am I trying to use lock() in a way I'm not supposed to? Have I misunderstood it's purpose? How can I prevent InvalidOperationExceptions?
推荐答案
我最终找到了这个问题的根源.我把锁移到了位图属性的getter/setter方法中,并实现了一个深拷贝"的方法来尽快释放这个属性.
I eventually got to the bottom of this. I moved the locks into the getter / setter methods of the bitmap property and implemented a "deep copy" method to free up the property as soon as possible.
private static object syncRoot = new Object(); private Bitmap _bm; private Bitmap bm { get { lock (syncRoot) return this._bm.DeepClone(); } set { lock (syncRoot) { this._bm = value.DeepClone(); } } }
DeepClone() 扩展方法来自另一个问题的答案:
The DeepClone() extension method was cribbed from an answer to another question:
public static T DeepClone<T>(this T a) { using (MemoryStream stream = new MemoryStream()) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, a); stream.Position = 0; return (T)formatter.Deserialize(stream); } }