基本上我想做的就是这个伪代码
List<DatabaseRecord> records;
List<ChangedItem> changedItems;
Parallel.ForEach<DatabaseRecord>(records, (item, loopState) =>
{
if (item.HasChanged)
{
lock (changedItems)
{
changedItems.Add(new ChangedItem(item));
}
}
});
但我担心的是锁定changedItems。虽然它有效,但我听说它必须一遍又一遍地序列化锁定的对象。有没有更好的方法呢?
答案 0 :(得分:9)
为什么不使用PLinq呢?无需锁定:
changedItems = records.AsParallel()
.Where(x => x.HasChanged)
.Select(x => new ChangedItem(x))
.ToList();
由于您要投射到ChangedItem
的新列表并且没有任何副作用,这将是我认为的方式。
答案 1 :(得分:1)
我认为锁定列表不会导致列表序列化/反序列化,因为锁定发生在所有对象上可用的私有字段上(syncBlockIndex)。但是,锁定的recommended way是创建一个专门用于锁定的私有字段:
object _lock = new object();
这是因为您可以控制锁定的内容。如果您通过属性发布对列表的访问权限,那么控件之外的代码可能会锁定该对象,从而引入死锁情况。
关于PLinq,我认为决定使用它取决于你的主机和负载是什么样的。例如,在ASP.NET中,PLINQ is more processor hungry可以更快地完成它,但代价是不再处理其他Web请求。无可否认,语法更加清晰。
答案 2 :(得分:1)
您似乎在单线程中使用此代码? 如果它是单线程,则不需要锁定。 删除行“lock(changedItems)”时它是否正常工作 @BrokenGlass发布的代码更清晰易懂。
答案 3 :(得分:1)
您可以为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();
}
}