可以接收委托并每N次调用一次的对象

时间:2019-01-24 23:20:22

标签: c# .net wpf asynchronous architecture

我如何创建一个由代理堆栈组成的异步结构,并弹出它们并每N毫秒调用一次它们?

现在的问题是,我有很多委托在ui上调用更改,这会导致ui冻结,因此,如果堆栈不为空,如何使此委托每N ms调用一次。

现在我有了

    class CallbackRestriction
    {
        private Stack<KeyValuePair<Action<ImageWrapper>, ImageWrapper>> _callbackList = 
            new Stack<KeyValuePair<Action<ImageWrapper>, ImageWrapper>>();

        public void AddCallback(Action<ImageWrapper> action, ImageWrapper payload)
        {
            _callbackList.Push(new KeyValuePair<Action<ImageWrapper>, ImageWrapper>(action, payload));
        }

        private async Task CallbackEmitLoop()
        {
            while (true)
            {
                await Task.Delay(TimeSpan.FromMilliseconds(20));

                try
                {
                    var callback = _callbackList.Pop();
                    callback.Key.Invoke(callback.Value);
                }
                catch (Exception e)
                {
                    await Task.Delay(200);
                }
            }
        }
    }

但是如何使CallbackEmitLoop在后​​台启动?或任何其他解决方案?

更新1

我不需要Dispather计时器,因为它已被WPF所限制,也许对于“计时器”而言,我应该使用同步上下文。而且从其他上下文调用我的集合也没有问题,因为可以使集合为并发就绪。我需要一个类似阀门的东西,一旦添加代表,它将限制调用代理。所以我在上面描述问题的方式可以一次获得很多“更新”(代理),如果我仅应用它们(调用委托),则ui线程将花费大量时间,这将导致冻结,因此我应该以某种方式保留时间,然后再应用下一次“更新”。

1 个答案:

答案 0 :(得分:0)

这是一种方式。下面的代码使用您的CallbackRestriction类和ImageWrapper的虚拟实现。我已经公开了CallbackEmitLoop方法,以便我的窗口可以使用Task.Run启动它。

因为我在窗口中维护了委托发射器实例,所以只要窗口处于活动状态,它将一直运行。真正的应用可能会通过其他一些服务类别运行它。

如果回调需要与WPF UI元素一起使用,则回调需要使用Dispatcher在UI线程上调用代码,因为Task在线程池线程上运行,并且任何委托调用也将在该线程上运行。

关于这可能是一个重复问题的评论,OP正在询问如何让正在运行的Task调用与UI交互的委托,而DispatcherTimer当然是一种合理的方法,但它并未解决OP的问题,也没有提供关于为什么DispatcherTimer将是更合适的实现的解释。

./gradlew