【问题标题】:Are linq operations on concurrent collections thread safe?并发集合上的 linq 操作是线程安全的吗?
【发布时间】:2014-12-19 16:08:22
【问题描述】:

例如下面的代码是线程安全的:

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

    }
}

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

【问题讨论】:

  • 线程在哪里?
  • 您可能在 asp.net 应用程序中拥有它,并且多个线程将访问它而无需您手动创建它们
  • 您的代码中没有多线程,但如果您在多线程环境中,ConcurrentQueue 绝对是一个不错的选择,因为它提供了对队列的安全访问。..
  • @pollirrata,当有问题的数据结构在当时和那里创建并且显然其他线程无法访问时,这无关紧要。
  • 修改任务中的队列是没有意义的,除非它是一个示例。这些操作花费的时间最短,在后台运行它们没有意义。

标签: c# linq concurrency


【解决方案1】:

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

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

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

【讨论】:

  • 谢谢,这就是我所追求的。因此,如果我希望 LINQ 操作和集合修改互斥,那么我必须实现自己的锁定集合。
  • 这取决于。例如,将队列包装在阻塞集合中并将出队公开为可枚举可能很有用。然后,您可以对其执行 linq 操作,但仅在队列为空时阻塞。或者确实根本不阻塞,而是做其他事情,然后再回来重复 linq 操作。这将同时修改和做 linq 的东西,没有锁定和完全线程安全。
  • 所以这意味着需要一个锁来避免快照包含更长的 LINQ 查询中的陈旧数据?否则你仍然容易受到多线程问题的影响。
【解决方案2】:

是的,但是……

让我们举个例子:

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

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

因此,它是线程安全的。

现在呢?

您在// Do something 的代码大概只有在队列中有与testGuid 匹配的元素时才会执行。不幸的是,我们不知道这是否属实,因为赫拉克利特的时间流已经继续前进,而我们所知道的是那里有这样一个指导。

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

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

【讨论】:

    【解决方案3】:

    是的,根据documentation

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

    【讨论】:

    • This documentation 会更有用,因为它专门讨论了 LINQ 将使用的集合上的方法调用。
    猜你喜欢
    • 2014-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-10
    • 1970-01-01
    • 1970-01-01
    • 2021-07-10
    相关资源
    最近更新 更多