(1)如果在两个不同的线程上调用交换和迭代函数,在迭代时可以安全地交换吗?
(2)我真的需要互锁的交换,因为读/写入引用是原子的(由CLR保证),假设对于弱记忆模型而言,释放语义需要互锁?
class someClass
{
public void Swap()
{
Queue<SomeType> newQueue = new Queue<SomeType>();
var oldQueue = Interlocked.Exchange(ref queue, newQueue);
//Perform operation on old queue
}
public void Iterate()
{
foreach(var someTypeObject in queue)
{
//Do Something
}
}
Queue<SomeType> queue = new Queue<Sometype>();
}
假设您不在 Swap
中修改 oldQueue
(即在枚举集合时修改集合),则是的,是的。您的foreach
扩展为:
{
IEnumerator e = ((IEnumerable)(queue)).GetEnumerator();
try {
Sometype someTypeObject;
while (e.MoveNext()) {
someTypeObject = (Sometype)e.Current;
// loop body
}
}
finally {
// Dispose e
}
}
摘自C#规范,第8.8.4节:foreach语句
换句话说,Iterate
中的foreach循环调用queue.GetEnumerator()
,从那时起,使用queue
的IEnumerator
的引用。更换queue
指向的内容不再对循环中的枚举器产生任何影响。
关于您的第二个问题,仅当您打算使用交换的旧值时,才需要使用Interlocked.Exchange
(并希望是原子)。否面
这取决于您在Oldqueue上执行的操作;如果您在迭代的中间交换,则迭代仍将在旧队列上进行,因此,如果您在旧队列上执行突变操作,则可能会破坏迭代。这应该在另一个方向上安全(如果您在交换中间进行迭代),因为迭代将在新的(空)队列上进行,尽管目前尚不清楚您想使用空排队。
通常,对于性能,很少需要无锁的操作,并且可能会弹出许多微妙的问题。您仍然必须谨慎使用锁,但是与试图与互锁做聪明的事情相比,它们咬您的可能性要小得多。只需说明您发布的内容,就很难说出适当的锁定设计是什么,但要考虑是否可能的边际性能改进值得不值得无锁设计的风险和可维护性。