Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>I've decided on a custom solution here... The locks are messy and undesired but at the moment, this does the job I want.</p> <pre><code>public interface ITaskExecutionQueue { Task&lt;TResult&gt; QueueTask&lt;TResult&gt;(Func&lt;Task&lt;TResult&gt;&gt; taskGenerator); Task&lt;TResult&gt; QueueTask&lt;TResult&gt;(Task&lt;Task&lt;TResult&gt;&gt; taskGenerator); int OutstandingTaskCount { get; } event EventHandler OutstandingTaskCountChanged; } /// This class ensures that only a single Task is executed at any one time. They are executed sequentially in order being queued. /// The advantages of this class over OrderedTaskScheduler is that you can use any type of Task such as FromAsync (I/O Completion ports) /// which are not able to be scheduled using a traditional TaskScheduler. /// Ensure that the `outer` tasks you queue are unstarted. E.g. &lt;![CDATA[ /// _taskExeQueue.QueueTask(new Task&lt;Task&lt;TResult&gt;&gt;(() =&gt; StartMyRealTask())); /// ]]&gt; class OrderedTaskExecutionQueue : ITaskExecutionQueue { private readonly Queue&lt;Task&gt; _queuedTasks = new Queue&lt;Task&gt;(); private Task _currentTask; private readonly object _lockSync = new object(); /// &lt;summary&gt; /// Queues a task for execution /// &lt;/summary&gt; /// &lt;typeparam name="TResult"&gt;&lt;/typeparam&gt; /// &lt;param name="taskGenerator"&gt;An unstarted Task that creates your started real-work task&lt;/param&gt; /// &lt;returns&gt;&lt;/returns&gt; public Task&lt;TResult&gt; QueueTask&lt;TResult&gt;(Func&lt;Task&lt;TResult&gt;&gt; taskGenerator) { return QueueTask(new Task&lt;Task&lt;TResult&gt;&gt;(taskGenerator)); } public Task&lt;TResult&gt; QueueTask&lt;TResult&gt;(Task&lt;Task&lt;TResult&gt;&gt; taskGenerator) { Task&lt;TResult&gt; unwrapped = taskGenerator.Unwrap(); unwrapped.ContinueWith(_ =&gt; { EndTask(); StartNextTaskIfQueued(); }, TaskContinuationOptions.ExecuteSynchronously); lock (_lockSync) { _queuedTasks.Enqueue(taskGenerator); if (_currentTask == null) { StartNextTaskIfQueued(); } } TaskCompletionSource&lt;TResult&gt; tcs = new TaskCompletionSource&lt;TResult&gt;(); tcs.TrySetFromTaskIncomplete(unwrapped); OutstandingTaskCountChanged.Raise(this); return tcs.Task; } private void EndTask() { lock (_lockSync) { _currentTask = null; _queuedTasks.Dequeue(); } OutstandingTaskCountChanged.Raise(this); } private void StartNextTaskIfQueued() { lock (_lockSync) { if (_queuedTasks.Count &gt; 0) { _currentTask = _queuedTasks.Peek(); _currentTask.RunSynchronously(); } } } /// &lt;summary&gt; /// Includes the currently executing task. /// &lt;/summary&gt; public int OutstandingTaskCount { get { lock (_lockSync) { return _queuedTasks.Count; } } } public event EventHandler OutstandingTaskCountChanged; } </code></pre> <p>Takes in an unstarted <code>Task&lt;Task&lt;TResult&gt;&gt;</code> - this allows the queue to decide when to execute it and begin the <code>FromAsync</code> call (which is the inner task). Usage:</p> <pre><code>Task&lt;Task&lt;TResult&gt;&gt; queueTask = new Task&lt;Task&lt;TResult&gt;&gt;(() =&gt; Task.Factory.FromAsync(beginAction, endAction)); Task&lt;TResult&gt; asyncCallTask = _taskExecutionQueue.QueueTask(queueTask); </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload