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