线程和计时器的奇怪行为

时间:2014-03-07 19:24:37

标签: c# multithreading producer-consumer blockingcollection

我解释了我的情况。

我有一个生产者1到N的消费者模式。我正在使用阻塞集合,一切都运行良好。做一些测试我发现了这种奇怪的行为:

我正在测试我的数据处理对我的消费者的影响。 我注意到这些奇怪的东西,下面你会发现我的操作代码被清除了,并产生了奇怪的行为。

我有1位制作人的4位消费者。 对于大多数数据,控制台不打印任何东西,因为ts = 0(它在一个勾号下)但随机(在每1到5秒之间)它绘制这样的东西(不是按照这个非常特定的顺序,但是相同类型):

10000
20001
10000
30002
10000
40003
10000
10000

大约1个小时,大约是10,000个小时。始终为格式(N)000(N-1)的数字 请注意,我消耗的BlockingCollection是根据一些在随机时间完全发生的网络事件来填充的。这里没什么经常的。

时机几乎是完美的,总是10,000个刻度的倍数。

这背后可能是什么?谢谢!

    while(IsAlive)
    {
            DataToFieldMapping item;
            try
            {
                _CollectionToConsume.TryTake(out item, -1);
            }
            catch
            {
                item = null;
            }
            if (item != null)
            {
                    long ts = (DateTime.Now.Ticks - item.TimeStamp.Ticks);
                    if(ts>10)
                       Console.WriteLine(ts);
            }
     }

2 个答案:

答案 0 :(得分:4)

这里发生的事情是DateTime.Now的精确度相当有限。它没有给你时间到最近的刻度线。它只会在每10,000个刻度左右更新一次,这就是为什么你通常会在打印件中看到10k刻度的倍数。

如果您真的想要更好地了解这些事件的持续时间,请使用StopWatch类,它具有更高的精度。也就是说,StopWatch只是一个诊断工具(因此它在Diagnostics命名空间中)。您应该只使用它来帮助您诊断正在发生的事情,并且应该在生产代码中使用它。

另一方面,根本没有必要在这里使用计时器。您似乎正在创建几个正在轮询BlockingCollection新内容的消费者。没有理由这样做。他们可以简单地阻止,直到收集有物品。 (因此名称为BlockingCollection

最简单的方法是让消费者简单地这样做:

foreach(var item in _CollectionToConsume.GetConsumingEnumerable())
   ProcessItem(item);

然后在后台线程中运行该代码。

答案 1 :(得分:1)

如果您编写以下内容并运行,您将看到刻度不会一对一滚动,而是相对较大的块b / c刻度分辨率实际上要小得多。

for(int i =0; i< 100; i++)
{
    Console.WriteLine(DateTime.Now.Ticks);
}

使用Stopwatch类测量性能,因为它使用了更适合此目的的高分辨率计时器。