Tuesday, September 6, 2016

Firing .Net events in separate thread

Suppose we are developing a library which has some events and we want our customers to handle the events in separate thread to make sure the UI is responsive. How can we do that?

Here is our class in the library which fires event every one second

class Clock
    {
        public event EventHandler<int> SecondChanged;
        DispatcherTimer timer;
        public Clock()
        {
            this.timer = new DispatcherTimer();
            this.timer.Interval = TimeSpan.FromSeconds(1);
            this.timer.Tick += Timer_Elapsed;
            this.timer.Start();
        }
 
        private void Timer_Elapsed(object sender, EventArgs e)
        {
            this.OnSecondChanged(DateTime.Now.Second);
        }
 
        protected virtual void OnSecondChanged(int second)
        {
            if(SecondChanged != null)
            {
                SecondChanged(this, second);
            }
        }
    }

Below is the code for handling the event normally. It handles the event in same thread and if it takes more time the UI will be unresponsive.
private void Clk_SecondChanged(object sender, int e)
{
    Log($"Event handler 1 - Second - {e}");
}

Lets see some approaches to handle event in separate thread.

Approach 1 - Guidelines to customers

Asking the customers to handle event handler in separate new thread or via ThreadPool is the simplest approach. But we don't get any guarantee that all are doing it right.
private void Clk_SecondChanged(object sender, int e)
{
    ThreadPool.QueueUserWorkItem((state) =>
    {
        Log($"Event handler 1 - Second - {e}");
    });
}

Approach 2 - Fire our event handlers in separate thread

Here we make sure each event handlers are executed in separate thread from the library itself. Below is the change required on firing method.
protected virtual void OnSecondChanged(int second)
{
    if (this.SecondChanged != null)
    {
        foreach (Delegate d in SecondChanged.GetInvocationList())
        {
            EventHandler<int> handler = d as EventHandler<int>;
            IAsyncResult ar = handler.BeginInvoke(this, second, nullnull);
            handler.EndInvoke(ar);
        }
    }
    
}
Happy coding...

No comments: