如何使用 1 个计时器排队并与另一个计时器取消排队

本文关键字:排队 计时器 另一个 取消 何使用 | 更新日期: 2023-09-27 18:32:11

我需要以大约 4 到 8 毫秒的间隔将项目排队到队列中。

另外,我的 UI 层需要以大约 33 毫秒的间隔取消排队、处理和显示来自这些项目的信息(它可能会在该时间间隔内多次取消排队)。

不太确定我应该使用计时器和队列的什么组合来使其工作。

我认为我应该使用队列的 ConcurrentQueue 类,但是我应该使用什么计时器机制来排队和出列?

更新:我最终选择了布莱恩·吉迪恩(Brian Gideon)和阿尔贝托(Alberto)的答案。

在不赘述所有细节的情况下,我所做的是:

我使用以下计时器来制作我的 4ms 计时器和 33ms 计时器。(http://www.codeproject.com/Articles/98346/Microsecond-and-Millisecond-NET-Timer)

我的 4ms 计时器从高速摄像机读取数据,进行少量处理并将数据排队到 ConcurrentQueue 中。

我的 33ms 计时器将所有项目从队列中取消排队,对每个项目进行更多处理,并将数据发送到另一个对象,该对象计算某个给定间隔内的滚动平均值。 (队列用于管理滚动平均值。

在 CompositionTarget.Rendering 事件中,我从滚动平均对象中获取值,并将它们绘制在自定义折线图控件上。

我提到了 UI 的 33ms,因为这些数据被输入到实时图中。 33ms 大约是 30 fps...任何比这慢的东西都会丢失一些平滑度。

我最终也使用了ConccuentQueue。效果很好。

CPU 受到一点打击。我认为这是由于高性能计时器。

谢谢大家的帮助。

如何使用 1 个计时器排队并与另一个计时器取消排队

这些是一些非常严格的时间要求。我质疑 UI 更新的 ~33ms 值。UI的更新速度不应该超过人类的感知速度,即使这样,也可能是矫枉过正。

相反,我要做的是使用生产者-消费者管道。

Producer -> Processor -> UI

在上面的原始插图中,生产者将执行生成消息并将它们排队的步骤。处理器将监视此队列,并对消息进行非 UI 相关处理。处理完成后,它将生成消息,其中包含更新 UI 线程所需的足够信息。此管道中的每个步骤都将在指定的线程上运行。我假设您确实需要两个不同的间隔(分别为 4 毫秒和 33 毫秒)。我建议您为 UI 添加第三个。轮询间隔可能是:

~4ms -> ~33ms -> 500ms

我故意使用波浪号 (~) 来强调这样一个事实,即在 .NET 中很难实现较低的间隔计时。您可能偶尔可以达到 33 毫秒,但使用 BCL 中内置的任何计时器,任意"刻度"群体的标准偏差将非常高。当然,4ms是不可能的。

您将需要尝试多媒体计时器或其他 HPET(高性能事件计时器)机制。其中一些机制使用特殊的硬件。如果你走这条路,那么你可能会更接近那个4ms的目标。不过不要指望奇迹。CLR 将从一开始就对你不利(垃圾回收)。

请参阅Jim Mischel的答案 这里 关于你的一些选择的很好的文章。

您可以使用一个 DispatcherTimer 将元素取消排队并将它们发布到 UI,并使用另一个 Timer 进行排队。

例如:

class Producer
{
    public readonly Timer timer;
    public ConcurrentQueue<int> Queue {get;private set;}
    Producer()
    {
        timer = new Timer(Callback, null, 0, 8);
        Queue = new Concurrent<int>();
    }
    private void Callback(object state)
    {
        Queue.Enqueue(123);
    }
}
class Consumer
{
    private readonly Producer producer;
    private readonly DispatcherTimer timer;
    Consumer(Producer p)
    {
        producer = p;
        timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromMilliseconds(33);
        timer.Tick += new EventHandler(dispatcherTimer_Tick);
        timer.Start();
    }
    private void dispatcherTimer_Tick(object sender, EventArgs e)
    {
        int value;
        if(producer.Queue.TryDequeue(out value))
        {
            // Update your UI here
        }
    }
}

由于您正在处理UI,因此您可以使用几个DispatcherTimer而不是经典计时器。此计时器专为与 UI 交互而设计,因此您的队列应该能够毫无问题地排队/取消排队。