Foreach循环和带有锁的变量
本文关键字:变量 循环 Foreach | 更新日期: 2023-09-27 18:02:56
基本上我要做的就是这个psuedo代码
List<DatabaseRecord> records;
List<ChangedItem> changedItems;
Parallel.ForEach<DatabaseRecord>(records, (item, loopState) =>
{
if (item.HasChanged)
{
lock (changedItems)
{
changedItems.Add(new ChangedItem(item));
}
}
});
但是我担心的是对changedItems的锁定。虽然它可以工作,但我听说它必须一遍又一遍地序列化锁定的对象。有更好的方法吗?
为什么不用PLinq呢?不需要锁:
changedItems = records.AsParallel()
.Where(x => x.HasChanged)
.Select(x => new ChangedItem(x))
.ToList();
既然你正在投射一个新的ChangedItem
列表,并且没有任何副作用,我认为这将是一种方式。
您是否可以为您的changedItems使用ConcurrentCollection ?比如ConcurrentQueue?那你就根本不需要锁了。
更新:
关于ConcurrentQueue, Enqueue不会阻塞线程以保证操作线程的安全。它保持在用户模式,使用SpinWait…
public void Enqueue(T item)
{
SpinWait wait = new SpinWait();
while (!this.m_tail.TryAppend(item, ref this.m_tail))
{
wait.SpinOnce();
}
}
我不认为列表上的锁定会导致列表序列化/反序列化,因为锁定发生在所有对象(syncBlockIndex)上可用的私有字段上。但是,推荐的锁定方式是创建一个专用的用于锁定的私有字段:
object _lock = new object();
这是因为您可以控制所锁定的内容。如果您通过属性发布对列表的访问,那么您控制之外的代码可能会在该对象上获取锁,从而引入死锁情况。
关于PLinq,我认为决定使用它取决于你的主机和负载是什么。例如,在ASP。PLINQ对处理器的需求更大,这将更快地完成任务,但代价是占用处理其他web请求的时间。不可否认,语法要干净得多。
似乎你在单线程中使用此代码?如果是单线程,则不需要锁。当你删除"lock (changedItems)"一行时,它是否正常工作?@BrokenGlass发布的代码更清晰,更容易理解。