0

How can I create an async structure that will be consist of stack of delegates and popping them and invoke each of them every N ms?

The problem is now I have lot delegates that invoke changes on ui and it causes ui freezing so how to make this delegates invoking every N ms if stack is not empty.

Now I have this

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

But how can I make CallbackEmitLoop start in the background? Or any other solution for this?

Update 1

I do not need the dispather timer because is tighten with wpf and maybe for "timer" things I should use synchronization context. And I don't have problems with calling to my collection from others context because collection can be made concurrency ready. I need something like a valve that would restrict invoking delegates once they have been added. So how I described problem above I can get a lot of "updates"(delegates) at one time and if I just apply them(call delegates) the ui thread would be busy significant time that will cause freezing and because of this I somehow should keep times before apply next "update".

Matryoshka
  • 11
  • 2

1 Answers1

0

Here's one way. The code below uses your CallbackRestriction class and my dummy implementation of ImageWrapper. I've made the CallbackEmitLoop method public so that my window can start it with Task.Run.

Because I maintain the delegate emitter instance in my window, it will run as long as the window is alive. A real app would likely run it from some other service class.

The callback needs to use Dispatcher to invoke code on the UI thread if it needs to work with WPF UI elements because the Task runs on a thread pool thread, and any delegate invocations will run on that thread too.

Regarding the comment that this may be a duplication question, the OP is asking how to have a running Task invoke delegates that interact with the UI, and while DispatcherTimer is certainly a reasonable approach, it doesn't address the OP's question, nor does it offer an explanation as to why DispatcherTimer would be a more appropriate implementation.

// My dummy ImageWrapper
public class ImageWrapper
{
    public string Val { get; set; }
}

public partial class MainWindow
{

    private CallbackRestriction _restriction = new CallbackRestriction();

    public MainWindow()
    {
        InitializeComponent();

        _restriction.AddCallback(MyCallback, new ImageWrapper() {Val = "Hello"});

        Task.Run(_restriction.CallbackEmitLoop);
    }

    private void MyCallback(ImageWrapper wrapper)
    {
        // since the callback will be running on the 
        // thread associated with the task, if you
        // want to interact with the UI in the callback
        // you need to use Dispatcher
        Dispatcher.BeginInvoke(new Action(() =>
        {
            Debug.WriteLine(wrapper.Val);
        }));
    }
}
Kevin Walsh
  • 114
  • 8