Thursday, January 15, 2015

Client side threading in WPF (without Dispatcher)

Create a TaskItem class with 4 props:
1. Task name (string)
2. Action (Action)
3. int Priority
4. State (object)

Then create a static TaskProcessor class:
* has a dictionary of TaskItems as value and string as key
* static 'Current' property like this.

private TaskProcessor _current;  
 private int _threadID;  
 public static TaskProcessor Current  
 {  
   get  
   {  
      if (_current == null)  
      {  
        //lock to make thread safe  
        lock (typeof(TaskProcessor))  
        {  
           //create a new instance  
           _current = new TaskProcessor();  
           _threadID = Thread.CurrentThread.ManagedThreadId;  
        }  
      }
      return _current;  
   }  

Create a method in TaskProcessor class like this:

public void AddTaskInQueue(string taskName, Action<object> action, object state, int priority)  
 {  
   lock (this)  
   {  
      TaskItem ti = new TaskItem(this) { TaskName = taskName, Action = action, State = state, Priority = priority };  
      TaskItem currentTask = null;  
      if (_tasks.TryGetValue(taskName, out currentTask))  
      {  
        _tasks[taskName] = ti;  
      }  
      else  
      {  
        _tasks.Add(taskName, ti);  
      }  
      // Cancel the previous task.  
      if (currentTask != null)  
      {  
        //not in scope of this post
        currentTask.CancelTask();  
      }  
      // Identify and Cancel all other lower or equal priority tasks.  
      // Priority = 0 tasks are independent tasks  
      if (priority != 0)  
      {  
        var lowPriorityTasks = (from task in m_queue  
                       where task.Value.Priority > priority && task.Value.TaskName != taskName  
                       select task);  
        foreach (KeyValuePair<string, TaskItem> task in lowPriorityTasks)  
        {  
           task.Value.CancelTask();  
        }  
      }  
      ti.StartTheTask();  
   }  
 }  


Usage in code (upto what we have done till now) will be like this:

TaskProcessor.Current.AddTaskInQueue("SomeTaskName", action =>  
       {  
         //make call to svc to get data  
         ServiceResponse results = GetDataFromService();  
         //update UI after we get the results..  
         //this is covered later in this blog  
       }, null, 0);  



TaskItem class code:

private TaskProcessor _taskprocessor;  
     private SynchronizationContext _synchronizer;  

public TaskItem(TaskProcessor taskprocessor)
        {
            _taskprocessor taskprocessor;
            _synchronizer= new TaskSynchronizationContext(SynchronizationContext.Current);

        }

     public void StartTheTask()  
     {  
       ThreadPool.QueueUserWorkItem(state =>  
       {  
         TaskItem ti = (TaskItem)state;  
         try  
         {  
           ti._taskprocessor.StartTask(ti);  
           ti._taskprocessor.SynchronizationContextValue(ti._synchronizer);  
           ti.ExecuteTask();  
         }  
         catch (Exception ex)  
         {  
           //log ex  
         }  
         finally  
         {  
           try  
           {  
             ti._taskprocessor.EndTask(ti);  
           }  
           finally  
           {  
             ti._taskprocessor.SynchronizationContextValue(null);  
           }  
         }  
       }, this);  
     }  
     private void ExecuteTask()  
     {  
       if (Action != null)  
       {  
         Action.Invoke(State);  
       }  
     }  

Create a TaskProcessorSynchronizationContext derived from SynchronizationContext class. Override all the methods where the sync object got from the ctor and call the base methods.

 public class TaskSynchronizationContext : SynchronizationContext  
   {  
     private SynchronizationContext _synctx;  
     public TaskSynchronizationContext(SynchronizationContext synctx)  
     {  
       _synctx = synctx;  
       SetSynchronizationContext(synctx);  
     }  
     public override SynchronizationContext CreateCopy()  
     {  
       return _synctx.CreateCopy();  
     }  
     //..override all methods as above  
     public override void Post(SendOrPostCallback d, object state)  
     {  
       _synctx.Post(d, state);  
     }  
     public override void Send(SendOrPostCallback d, object state)  
     {  
       string status = "Success";  
       string tid = System.Threading.Thread.CurrentThread.ManagedThreadId.ToString();  
       try  
       {  
         _synctx.Send(x =>  
         {  
           object[] temp = (Object[])x;  
           try  
           {  
             d.Invoke(temp[1]);  
           }  
           finally  
           {  
           }  
         }, state);  
       }  
       catch (Exception)  
       {  
         status = "Failed";  
       }  
       finally  
       {          
       }  
     }  
     public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)  
     {  
       return _synctx.Wait(waitHandles, waitAll, millisecondsTimeout);  
     }  
   }  

Now the usage changes to this:

TaskProcessor.Current.AddTaskInQueue("SomeTaskName", action =>  
     {  
       //make call to svc to get data   
       ServiceResponse results = GetDataFromService();  
       //update UI after we get the results..   
       TaskProcessor.Current.Synchronizer.Send(uiAction =>  
       {  
         ProcessResultsAndUpdateUI(results);  
       }, null);  
     }, null, 0);  



No comments :