patterncsharpMinor
Chaining asynchronous tasks that must run sequentially
Viewed 0 times
mustasynchronouschainingtasksthatrunsequentially
Problem
I want to implement an
At any point in time, at most one [update submitted to an] Agent is
being executed.
My current implementation uses the TPL. The
The first thread to send an action will see
This (seems to) work. Only one submitted action is ever running at a given moment. Additionally, tasks are potentially run on different threads. I don't want a dedicated thread for updates (and I don't want to lock up a
Agent-like object in C#. An Agent wraps some memory location (ideally storing an immutable object) and receives updates to that location. All these updates are performed asynchronously, but sequentially. In other wordsAt any point in time, at most one [update submitted to an] Agent is
being executed.
My current implementation uses the TPL. The
Agent keeps a reference to the last update that has been or must be performed, wrapped in a Task. When submitted a new update, you wrap the update in a Task, get a reference to the last Task and replace it, atomically, then invoke ContinueWith on that last Task with this new update Task (everything submitted through a TaskScheduler).public class Agent where T : class
{
private T wrappedValue;
private readonly TaskScheduler scheduler;
private Task lastTask;
public Agent (T value, TaskScheduler scheduler)
{
this.wrappedValue = value;
this.scheduler = scheduler;
}
public void Send (Action operation)
{
Task task = new Task ((action) => ((Action)action) (wrappedValue), operation);
Task localCurrent;
do {
// always append to the end
localCurrent = lastTask;
} while(Interlocked.CompareExchange (ref lastTask, task, localCurrent) != localCurrent);
if (localCurrent == null) {
task.Start (scheduler);
} else {
localCurrent.ContinueWith ((currentTask, scheduler) => {
task.Start ((TaskScheduler)scheduler);
}, scheduler, scheduler);
}
}
[...]
}The first thread to send an action will see
null in the field and so needs to Start() the Task directly.This (seems to) work. Only one submitted action is ever running at a given moment. Additionally, tasks are potentially run on different threads. I don't want a dedicated thread for updates (and I don't want to lock up a
TaskScheduler thread). Solution
-
I would use
-
I think you can modify your lambda expression to get a current task:
Though I am not too experienced with TPL, so there might be an easier way.
I would use
lock instead of Interlocked, because its much easier to follow and debug. Unless you are after some kind of micro optimization, I think you should keep it simple:lock (lock)
{
if (lastTask == null)
{
task.Start(scheduler);
}
else
{
lastTask.ContinueWith(...);
}
lastTask = task;
}-
I think you can modify your lambda expression to get a current task:
Task task = null;
task = new Task ((action) =>
{
CurrentTask = task;
((Action)action) (wrappedValue);
}, operation);Though I am not too experienced with TPL, so there might be an easier way.
Code Snippets
lock (lock)
{
if (lastTask == null)
{
task.Start(scheduler);
}
else
{
lastTask.ContinueWith(...);
}
lastTask = task;
}Task task = null;
task = new Task ((action) =>
{
CurrentTask = task;
((Action<T>)action) (wrappedValue);
}, operation);Context
StackExchange Code Review Q#97199, answer score: 3
Revisions (0)
No revisions yet.