对并发集合线程的linq操作是否安全?

时间:2014-12-19 16:08:23

标签: c# linq concurrency

例如以下代码线程安全:

ConcurrentQueue<Guid> _queue = new ConcurrentQueue<Guid>();
while(true)
{
for(int y = 0; y < 3; y++)
{
    if(y % 3 == 0)
    {
    System.Threading.Tasks.Task.Run(() => _queue.Enqueue(Guid.NewGuid()));
    }
    else if (y % 3 == 1)
    {
    Guid x;
    System.Threading.Tasks.Task.Run(() => _queue.TryDequeue(out x));
    }
    else if(y % 3 == 2)
    {
    System.Threading.Tasks.Task.Run(() =>
    {
        if (_queue.Any(t => t == testGuid))
        {
        // Do something
        }
    });

    }
}

编辑:显然标题不够清晰,所以更新了代码示例以包含实际的多线程行为,是的,上面的代码只是多线程行为的示例

3 个答案:

答案 0 :(得分:9)

LINQ操作是只读的,因此它们在所有集合上是线程安全的。当然,如果添加在WhereSelect方法中修改集合的代码,它们将不再是线程安全的。

线程安全集合确保修改是线程安全的,这在执行LINQ查询时并不是真正的问题。

不是安全的是在遍历期间修改集合。正常集合在修改它们时会使迭代器失效,而线程安全集合则不会。在某些情况下,(例如在ConcurrentQueue中)这是通过在迭代期间呈现数据的快照来实现的。

答案 1 :(得分:3)

是的,但是......

让我们举个例子:

if(_queue.Any(t => t == testGuid))
{
     // Do something
}

现在这不会,无论其他线程在做什么,都会以异常方式失败,除非采用文档化方式(在这种情况下意味着失败并出现任何异常),将_queue置于无效状态,或返回错误答案。

因此,它是线程安全的。

现在是什么?

如果队列中的某个元素与// Do something匹配,那么testGuid处的代码可能只会被执行。不幸的是,我们不知道这是否属实,因为Heraclitan的时间流已经开始,我们所知道的是 那里有一个Guid。

现在,这不一定无用。例如,我们可以知道队列当前只被添加到(例如,当前线程可能是唯一一个出列的线程,或者所有出列在我们知道不存在的特定条件下发生)。然后我们知道那里还有这样的指导。或者我们只想标记testGuid是否仍然存在。

但如果// Do something依赖于队列中testGuid的存在,并且队列正在从队列中出队,那么代码块作为一个整体不是线程安全的,尽管链接表达式是

答案 2 :(得分:2)

是的,根据documentation

  

System.Collections.Concurrent命名空间提供了几个   应该使用的线程安全的集合类来代替   System.Collections和中的相应类型   每当有多个线程时,System.Collections.Generic命名空间   同时访问该集合。