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的锁定。虽然它可以工作,但我听说它必须一遍又一遍地序列化锁定的对象。有更好的方法吗?

Foreach循环和带有锁的变量

为什么不用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发布的代码更清晰,更容易理解。