我有几个对象要发送到服务器,但我想确保这是将数据从 Stage 移动到 Upload 的唯一线程。以下代码在多线程环境中有效吗?
List<CounterInternal> UploadToServer = new List<CounterInternal>();
List<CounterInternal> StagingQueue = new List<CounterInternal>();
lock (this.UploadToServer)
lock (this.StagingQueue)
{
if (UploadToServer.Count == 0)
{
UploadToServer = StagingQueue.DoDeepCopyExtensionMethod();
// is the following line valid given that I have a Lock() on it?
StagingQueue = new List<CounterInternal>();
}
}
}
最佳答案
从技术上讲,是的,但这是个坏主意。考虑这个 C# 源文件:
using System;
class Foo {
static object foo = new object();
static void Main() {
lock (foo) {
foo = new object();
}
}
}
Main()
方法将编译为:
.method private static hidebysig
default void Main () cil managed
{
// Method begins at RVA 0x2100
.entrypoint
// Code size 35 (0x23)
.maxstack 3
.locals init (
object V_0)
IL_0000: ldsfld object Foo::foo
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: call void class [mscorlib]System.Threading.Monitor::Enter(object)
.try { // 0
IL_000c: newobj instance void object::'.ctor'()
IL_0011: stsfld object Foo::foo
IL_0016: leave IL_0022
} // end .try 0
finally { // 0
IL_001b: ldloc.0
IL_001c: call void class [mscorlib]System.Threading.Monitor::Exit(object)
IL_0021: endfinally
} // end handler 0
IL_0022: ret
} // end of method Foo::Main
这对应于以下来源(手工反编译):
static void Main() {
object V_0 = foo;
Monitor.Enter(V_0);
try {
foo = new object();
} finally {
Monitor.Exit(V_0);
}
}
因此被锁定的对象将存储在一个本地——这保证了即使存储在字段中的对象引用被替换,对象的监视器也会被释放。单独使用此技术不会产生死锁,并且已经在 Monitor.Enter()
上阻塞的任何其他线程将像往常一样继续阻塞,直到该线程释放锁。
但是,任何在您重新分配对象之后但在事件线程释放锁之前进入此方法的线程都将获取对新对象的锁 对象,因此锁 block 中可以同时有两个线程。
更好的解决方案是使用一个单独的对象并锁定它。我通常使用 System.Object
类(或只是 object
),因为它所做的只是充当互斥体。这将允许所有线程锁定同一个对象,同时允许更改其他对象引用。当您需要锁定以改变无法锁定的值类型时,这也是一种有用的技术。
关于c# - 我可以在 C# 中覆盖已被 Locked() 的对象吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4703835/