C# Foreach循环和带锁定的变量

C# Foreach循环和带锁定的变量,c#,task-parallel-library,C#,Task Parallel Library,基本上我想做的是这个psuedo代码 List<DatabaseRecord> records; List<ChangedItem> changedItems; Parallel.ForEach<DatabaseRecord>(records, (item, loopState) => { if (item.HasChanged) { lock (changedItems) {

基本上我想做的是这个psuedo代码

List<DatabaseRecord> records;

List<ChangedItem> changedItems;

Parallel.ForEach<DatabaseRecord>(records, (item, loopState) =>
{
    if (item.HasChanged)
    {
        lock (changedItems)
        {
            changedItems.Add(new ChangedItem(item));
        }
    }
});
列出记录;
列出变更事项;
Parallel.ForEach(记录,(项,循环状态)=>
{
如果(项目已更改)
{
锁(变更数据项)
{
添加(新的ChangedItem(项目));
}
}
});

但我担心的是锁定变革派。当它工作时,我听说它必须反复序列化锁定的对象。有更好的方法吗?

为什么不改用PLinq呢?无需锁定:

changedItems = records.AsParallel()
                      .Where(x => x.HasChanged)
                      .Select(x => new ChangedItem(x))
                      .ToList();

由于您正在投影到一个新的
ChangedItem
列表中,并且没有任何副作用,因此我认为这将是一种方法。

我不认为锁定列表会导致列表序列化/反序列化,因为锁定发生在所有对象(syncBlockIndex)上可用的私有字段上。但是,进行锁定的方法是创建一个专用字段,专门用于锁定:

object _lock = new object();
这是因为你可以控制你锁定的东西。如果您通过属性发布对列表的访问,那么您无法控制的代码可能会锁定该对象,从而导致死锁情况


关于PLinq,我认为决定使用它取决于您的主机和负载是什么样的。例如,在ASP.NET中,这将更快地完成它,但代价是将处理从服务其他web请求中移除。无可否认,语法要干净得多。

您似乎在单线程中使用此代码? 如果是单线程,则不需要锁。 当您删除“lock(changedItems)”行时,它工作正常吗
@BrokenGlass发布的代码更加清晰易懂。

您是否可以将ConcurrentCollection用于您的changedItems。类似ConcurrentQueue的东西?那你就根本不需要锁了

更新:

对于ConcurrentQueue,为了保证操作线程的安全,队列不会阻塞线程。它将保持在用户模式下,并使用SpinWait

public void Enqueue(T item)
{
    SpinWait wait = new SpinWait();
    while (!this.m_tail.TryAppend(item, ref this.m_tail))
    {
        wait.SpinOnce();
    }
}

为什么不锁定一个
对象
,并让任何其他对changeItems有效的代码也锁定该
对象
?当然,是常规线程应用程序。但不是TPL.+1-这是一条路-只要
ChangedItem
构造函数是线程安全的,并且不依赖于共享状态(以前的锁可以通过避免任何问题轻松“修复”),我希望我可以,但是我做不到,因为我在对象上做的代码比检查.haschange的代码要多…我相信ConcurrentQueue在引擎盖下是无锁的。我还认为TPL是通过这些并发集合实现的。明天我会检查w反射器。我需要在凌晨1:38停止评论。我的评论开始变得语无伦次了。