Search code examples
c#multithreadingscheduled-taskssynchronizationcontext

TaskScheduler - executing continuations


After a bit of reading about TaskScheduler on Parallel Programming with .NET it turns out that a TaskScheduler can:

  1. Schedule tasks - by using QueueTask method, which in the example above just Posts task execution to selected SynchronizationContext

  2. Schedule continuations through the lower-level scheduler associated with the currently running framework (SynchronizationContext).

Is 2. in any way related to CotninueWith method of Task? I obviously understand 1., but don't see how 2. is true for TaskScheduler. In what method of TaskScheduler would that happen?


Solution

  • Schedule continuations through the lower-level scheduler associated with the currently running framework (SynchronizationContext).

    I think you got it mixed a little. You can use a given TaskScheduler and execute a continuation on it, not the other way around. That is exactly what this snippet from the post you shared does:

    var ui = TaskScheduler.FromCurrentSynchronizationContext(); 
    var tf = Task.Factory; 
    
    blendedImage.ContinueWith(_ => 
    { 
        pictureBox1.Image = blendedImage.Result; 
    }, ui); 
    

    It tells the Task.ContinueWith to use the UI TaskScheduler (which was provided by calling TaskScheduler.FromCurrentSynchronizationContext()) earlier in order to invoke the continuation on a particular context, this time being the UI message loop.

    If you really want to get down to the details, when you pass a TaskScheduler to ContinueWith, it ends up passing it to a class called StandardTaskContinuation which has the following Run method that ends up calling TaskScheduler.InternalTaskQueue:

    internal void ScheduleAndStart(bool needsProtection)
    {
        if (needsProtection)
        {
            if (!this.MarkStarted())
            {
                return;
            }
        }
        else
        {
            this.m_stateFlags |= 65536;
        }
        if (Task.s_asyncDebuggingEnabled)
        {
            Task.AddToActiveTasks(this);
        }
        if (AsyncCausalityTracer.LoggingOn && 
           (this.Options & (TaskCreationOptions)512) == TaskCreationOptions.None)
        {
            AsyncCausalityTracer.TraceOperationCreation(
                CausalityTraceLevel.Required, this.Id, "Task: " +
                ((Delegate)this.m_action).Method.Name, 0uL);
        }
        try
        {
            this.m_taskScheduler.InternalQueueTask(this);
        }
        catch (ThreadAbortException exceptionObject)
        {
            this.AddException(exceptionObject);
            this.FinishThreadAbortedTask(true, false);
        }
        catch (Exception arg_93_0)
        {
            TaskSchedulerException ex = new TaskSchedulerException(arg_93_0);
            this.AddException(ex);
            this.Finish(false);
            if ((this.Options & (TaskCreationOptions)512) == TaskCreationOptions.None)
            {
                this.m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
            }
            throw ex;
        }
    }