【问题标题】:How to add items to a collection, while filtering it?如何在过滤时将项目添加到集合中?
【发布时间】:2015-10-30 08:25:39
【问题描述】:

我有一组日志事件。我让用户能够过滤掉不同的日志级别。在该过滤过程中,如果添加了新的日志事件,则会引发异常。我可以在过滤集合时锁定它,但这意味着任何新的日志事件都不会以正确的顺序添加,或者根本不会添加(?)。

过滤方式:

//Filters and returns a list of filtered log events
    private IEnumerable<LogEvent> FilterLogEvents()
    {
        return (from x in LogEvents
                where ((ViewDebugLogs == true) ? x.Level == "Debug" : false)
                || ((ViewErrorLogs == true) ? x.Level == "Error" : false)
                || ((ViewInfoLogs == true) ? x.Level == "Info" : false)
                select x);
    }

如果我只返回一个列表,如果它在查询中,也会发生同样的事情。

如何在添加到集合的同时使用它?这里的主要关注点是确保日志事件以正确的顺序添加到集合中,如果它被添加到中间过滤器中。因此,如果我不能同时添加日志事件,我可以以某种方式将它们排队并在之后正确插入它们吗?

【问题讨论】:

  • LogEvents是什么类型?
  • 尝试使用并发数据结构:msdn.microsoft.com/en-us/library/dd460718(v=vs.110).aspx。或者您可以尝试过滤列表的副本,而不是原始列表。
  • 这是一个包含事件相关信息的类型,例如级别,消息,时间戳...等我已经添加到帖子中
  • @douglasg14b 我想他问的是LogEvent*s*而不是LogEvent
  • @MatthewWatson 我猜,LogEvents 只是LogEvent 的集合。

标签: c# multithreading collections


【解决方案1】:

不,枚举时不能修改集合。可以复制集合,枚举副本,枚举过程中修改原始集合。

//the following example throws an exception
var numbers = GetTheNumbers();
foreach(var number in numbers)
{
    numbers.Add(1);   //exception thrown
}

//copy the collection first
var numbers = GetTheNumbers();
var copy = numbers.ToArray();
foreach(var number in copy)
{
    numbers.Add(1);  //safe
}

【讨论】:

    【解决方案2】:

    您可以在向其中添加项目的同时迭代一些并发集合。一个这样的集合是ConcurrentQueue&lt;T&gt;,如果满足以下条件,我认为它可能适合日志:

    • 您只想在日志末尾添加项目。
    • 您无需一次性清空它。 (而不是清空队列,您必须创建一个新的空队列,或者您必须循环调用 TryDequeue() 直到它为空)。

    请注意,当您迭代 ConcurrentQueue&lt;T&gt; 时,似乎计数是在迭代开始时读取的,并且在迭代期间没有更新,因此如果在迭代时添加了新项目,您将看不到它们(但添加项目不会导致异常)。

    这是一个演示程序:

    using System;
    using System.Collections.Concurrent;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace Demo
    {
        internal class Program
        {
            public static void Main()
            {
                var queue = new ConcurrentQueue<int>();
    
                Task.Run(() =>
                {
                    for (int i = 0; i < 1000; ++i)
                    {
                        queue.Enqueue(i);
                        Thread.Sleep(1);
                    }
                });
    
                Thread.Sleep(100);
                Console.WriteLine($"Count before loop: {queue.Count}");
    
                foreach (var i in queue)
                    Console.WriteLine($"{i}, n={queue.Count}");
            }
        }
    }
    

    【讨论】:

    • 啊!使用线程安全的集合是有意义的,我从不修改这个集合,只向它添加项目,所以效果很好。我只需要弄清楚如何将未在查询中捕获的日志条目获取到新的过滤集合。不过我很确定我可以处理。
    猜你喜欢
    • 2010-09-22
    • 1970-01-01
    • 1970-01-01
    • 2010-11-15
    • 1970-01-01
    • 1970-01-01
    • 2015-02-28
    • 2011-09-13
    • 1970-01-01
    相关资源
    最近更新 更多