public void Remove(T item)
{
locker.EnterWriteLock();
try
{
list.Remove(item);
}
}
以上是实例类的方法。假设实例是myObject
。
关于上述片段,我的问题是:
线程一调用myObject.Remove(A)
线程1执行locker.EnterWriteLock()
。
线程两次调用myObject.Remove(B)
。
线程1进入try块并执行list.Remove()
。
此时item
的价值是多少?即将{A}或B作为参数调用list.Remove()
?
答案 0 :(得分:3)
每个线程都有自己的调用堆栈,方法参数存储在该堆栈中。线程之间不共享堆栈。
在第一个线程的堆栈中,有一行表示正在调用Remove
并且包含“A”或对“A”的引用(取决于它是值还是引用类型)。
当线程2进入方法时,它具有自己的调用堆栈(线程1的堆栈在线程2运行时未使用),其中有一行指示Remove
的项目“B”的开始。然后该线程被挂起,它的调用堆栈未被使用,我们返回到线程1,其中项“A”是调用堆栈上的内容。
在未来的某个时刻,线程2将被重新激活,并且在它的调用堆栈上将有“B”项。
答案 1 :(得分:1)
方法参数在调用方法的线程的堆栈上分配。因此,每个线程都有自己的参数,它们不会相互影响。
答案 2 :(得分:0)
我怀疑你应该锁定的是list
变量。你在它上面调用Remove
方法,如果这个list
变量是某种非线程安全的类型,例如List<T>
,你可能会遇到麻烦,如果这个方法同时从myObject
的同一个实例上的多个线程(这是您的list
字段所在的位置)。就方法的论点而言,在方法的上下文中很难谈论它们的任何线程安全性。
答案 3 :(得分:0)
.Net不对参数提供任何继承锁定。但是,list.Remove(item)
不会修改item
(假设它是标准的System.Collections
或System.Collections.Generic
实现),并且使用不同的参数调用相同的方法两次会创建{{1}的两个副本(每个方法调用一个),而不是一个。
相反,我会锁定列表,因为大多数类的实例方法都不能保证是线程安全的。
答案 4 :(得分:0)
线程2将在locker.EnterWriteLock();
等待线程1执行locker.ExitWriteLock();
因此线程2在线程1调用list
locker.ExitWriteLock();
如果你担心参数item
,请确保它通过堆栈并且每个线程都有自己的一个,所以item
实际上不能被另一个线程“替换”...
所以答案是:如果线程1在线程2之前调用locker.EnterWriteLock();
,A
将在B
之前删除。
答案 5 :(得分:0)
item
的值将是A
参数的值。
只有在行locker.ExitWriteLock
执行后,该块才可用于第一个线程。