Wednesday, April 4, 2012

Accessing UI Thread Controls from Task using UI Thread Scheduler


While dealing with “Tasks” creating multiple which run concurrently, it might be required to access or update the UI the progress state. If the Task which is running a different thread other than UI thread, tries to access the controls (textbox or label, etc…) of UI thread, UI thread throws cross thread error.
To avoid this, we can either use the UI thread Synchronization context as posted in Thread Synchronization Context  or the UI Thread Scheduler can be used to access the control or functions in the UI Thread.
Below example demonstrates the same.
Form.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;
using System.Net;
using System.Collections.Specialized;
using System.Reflection.Emit;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// function which takes time to generate.
        /// </summary>
        /// <param name="root"></param>
        /// <returns></returns>
        public static double SumRootN(int root)
        {
            double result = 0;
            for (int i = 1; i < 100000; i++)
            {
                result += Math.Exp(Math.Log(i) / root);
            }
            return result;
        }
        private void btnLogSum_Click(object sender, EventArgs e)
        {
            try
            {
                int totTasks = 10;
                SynchronizationContext ct = SynchronizationContext.Current;
                lblResult.Text += Environment.NewLine + "--------------";
                //creating 10 tasks, calculating the "SumRootN" for each value from 1 to 10
                //and displaying the same in the UI.
                for (int i = 1; i <= totTasks; i++)
                {
                    int j = i;

                    TaskScheduler objScheduler = TaskScheduler.FromCurrentSynchronizationContext();
                    Task<double> objTask = Task.Factory.StartNew(
                           () =>
                           {
                               return SumRootN(j);
                           }
                       );
                    //using the synchronisation context of the UI Thread.
                    //objTask.ContinueWith(
                    //    (tsk) =>
                    //    {
                    //        ct.Post((o) => { lblResult.Text += Environment.NewLine + "Result is : " + o; }, tsk.Result);
                    //    });

                    //or
                    //using the scheduler of the UI Thread.
                    objTask.ContinueWith(
                        (tsk) =>
                        {
                            lblResult.Text += Environment.NewLine + "Result is : " + tsk.Result;
                        }, objScheduler);
                }
            }
            catch (Exception exp)
            {
                MessageBox.Show("Error : " + exp.Message);
            }
        }
    }
}



Output





No comments: