是否可以覆盖已在C#中锁定()的对象?
我有几个对象要发送到服务器,但我想确保这是唯一一个将数据从Stage移动到Upload的线程。以下代码在多线程环境中有效吗是否可以覆盖已在C#中锁定()的对象?,c#,.net,multithreading,C#,.net,Multithreading,我有几个对象要发送到服务器,但我想确保这是唯一一个将数据从Stage移动到Upload的线程。以下代码在多线程环境中有效吗 List<CounterInternal> UploadToServer = new List<CounterInternal>(); List<CounterInternal> StagingQueue = new List<CounterInternal>(); lock (th
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>();
}
}
}
List UploadToServer=newlist();
List StagingQueue=新列表();
锁定(this.UploadToServer)
锁定(此.StagingQueue)
{
if(UploadToServer.Count==0)
{
UploadToServer=StagingQueue.DoDeepCopyExtensionMethod();
//如果我有一个Lock(),那么下面这行有效吗?
StagingQueue=新列表();
}
}
}
这不是个好主意<代码>锁定对特定实例进行操作,在执行新建列表
时创建一个新实例。另一个线程可能会出现并锁定新的StagingQueue,而另一个线程仍在您认为是锁定的区域内
你应该:
lock
在另一个实例变量上(最好是readonly
一个),甚至可能是一个对象
实例,它的存在纯粹是为了作为被锁定的对象列表的新实例。例如,如果您只想清空列表,只需调用StagingQueue.Clear()
这不是个好主意<代码>锁定对特定实例进行操作,在执行
新建列表
时创建一个新实例。另一个线程可能会出现并锁定新的StagingQueue,而另一个线程仍在您认为是锁定的区域内
你应该:
lock
在另一个实例变量上(最好是readonly
一个),甚至可能是一个对象
实例,它的存在纯粹是为了作为被锁定的对象列表的新实例。例如,如果您只想清空列表,只需调用StagingQueue.Clear()
从技术上讲,是的,但这是个坏主意。考虑这个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
这对应于以下源(手动反编译):
因此,锁定的对象将存储在本地文件中——这保证了即使替换了存储在字段中的对象引用,也会释放对象的监视器。仅此技术不会产生死锁,并且在Monitor.Enter()
上已被阻止的任何其他线程将继续像往常一样被阻止,直到此线程释放锁为止
但是,任何在重新分配对象后但在活动线程释放锁之前进入此方法的线程都将获得新对象的锁,因此锁块中可能同时有两个线程。
更好的解决方案是使用单独的对象并锁定它。我通常使用类
System.Object
(或者只是Object
)的东西,因为它所做的只是充当互斥对象。这将允许所有线程锁定同一个对象,同时允许更改其他对象引用。当您需要锁定以改变无法锁定的值类型时,这也是一种有用的技术。从技术上讲,是的,但这不是一个好主意。考虑这个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
这对应于以下源(手动反编译):
因此,锁定的对象将存储在本地文件中——这保证了即使替换了存储在字段中的对象引用,也会释放对象的监视器。仅此技术不会产生死锁,并且在Monitor.Enter()
上已被阻止的任何其他线程将继续像往常一样被阻止,直到此线程释放锁为止
但是,任何在重新分配对象后但在活动线程释放锁之前进入此方法的线程都将获得新对象的锁,因此锁块中可能同时有两个线程。
更好的解决方案是使用单独的对象并锁定它。我通常使用类
System.Object
(或者只是Object
)的东西,因为它所做的只是充当互斥对象。这将允许所有线程锁定同一个对象,同时允许更改其他对象引用。当您需要锁定以改变无法锁定的值类型时,这也是一种有用的技术。您编写了代码,但花了时间发布了一个问题,因为……您找不到F5按钮?我正在测试它,但我认为可能会出现其他隐藏的东西。例如,锁可能无法使用,被覆盖后会消失,或者我会丢失数据。我在考虑边缘案例,但由于我是新手,可能我不知道该问什么。请不要逐字逐句地回答这个问题,看看我的意图……我并不是想让人觉得我过于讽刺。如果我真的道歉了,我只是说(也许这就是我学到的)你已经99%成功了,为什么不试试看呢?正如@Andy提到的,它会保留引用,所以在引用被锁定时将其更改不是一个好主意。@Brad:正如他所说的,他在询问边缘案例。完全正确的问题。您已经编写了代码,但是花了时间发布了一个问题,因为……您找不到F5按钮?我正在测试它,但我认为可能会出现其他隐藏的东西。例如,锁可能无法使用,被覆盖后会消失,或者我会丢失数据。我在考虑边缘案例,但由于我是新手,可能我不知道该问什么。请不要逐字逐句地回答这个问题