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

本文关键字:操作 是否 安全 linq 集合 线程 并发 | 更新日期: 2023-09-27 18:34:41

例如,以下代码线程是否安全:

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
        }
    });
    }
}

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

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

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

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

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

是的,但是...

让我们以你为例:

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

现在,无论其他线程在做什么,这都不会因异常而失败,除非以记录的方式(在这种情况下意味着失败并出现任何异常(,将_queue置于无效状态,或返回不正确的答案。

因此,它是线程安全的。

现在怎么办?

您的// Do something代码可能仅在队列中存在与testGuid匹配的元素时才执行。不幸的是,我们不知道这是否属实,因为赫拉克利坦的时间流已经向前移动,我们只知道那里这样一个吉德。

现在,这不一定没用。例如,我们可以知道队列当前仅被添加到(例如,也许当前线程是唯一取消排队的线程,或者所有取消排队都发生在我们知道没有到位的某些条件下(。然后我们知道那里还有这样的指导。或者我们可能只想标记testGuid是否仍然存在。

但是,如果// Do something取决于队列中是否存在testGuid,并且队列正在从中取消排队,则代码块作为一个整体不是线程安全的,尽管链接表达式是。

是的,根据文档

System.Collections.Concurrent 命名空间提供了多个 应用来代替 系统中的相应类型。集合和 System.Collections.Generic namespaces 只要有多个线程 同时访问集合。