none
Regarding usage of Task.Start() , Task.Run() and Task.Factory.StartNew() TPL RRS feed

  • Question

  • i just saw 3 routine regarding TPL usage which does same job

    here is the code

    public static void Main()
       {
          Thread.CurrentThread.Name = "Main";
    
            // Create a task and supply a user delegate by using a lambda expression. 
            Task taskA = new Task( () => Console.WriteLine("Hello from taskA."));
            // Start the task.
            taskA.Start();
    
            // Output a message from the calling thread.
            Console.WriteLine("Hello from thread '{0}'.", 
                              Thread.CurrentThread.Name);
            taskA.Wait();
       }
    
    public static void Main()
       {
          Thread.CurrentThread.Name = "Main";
    
          // Define and run the task.
          Task taskA = Task.Run( () => Console.WriteLine("Hello from taskA."));
    
          // Output a message from the calling thread.
          Console.WriteLine("Hello from thread '{0}'.", 
                              Thread.CurrentThread.Name);
          taskA.Wait();
       }
    
    public static void Main()
       {
          Thread.CurrentThread.Name = "Main";
    
          // Better: Create and start the task in one operation. 
          Task taskA = Task.Factory.StartNew(() => Console.WriteLine("Hello from taskA."));
    
          // Output a message from the calling thread.
          Console.WriteLine("Hello from thread '{0}'.", 
                            Thread.CurrentThread.Name);
    
          taskA.Wait();                  
       }

    i just do not understand why MS give 3 different way in TPL because they all work same

    Task.Start() , Task.Run() and Task.Factory.StartNew()

    tell me Task.Start() , Task.Run() and Task.Factory.StartNew() all used for same purpose or they have different significant ?

    when one should use Task.Start() and when one should use Task.Run() and when one should use Task.Factory.StartNew() ?

    please help me to understand their real usage as per scenario. thanks

    Thursday, April 16, 2015 7:22 PM

Answers

  • The Start instance method may be used if and only if the Task is in the Created state (i.e. Task.Status returns TaskStatus.Created).  And the only way a Task can be in the Created state is if the Task were instantiated using one of Task’s public constructors, e.g. "var t = new Task(someDelegate);”.

    It queues the Task to the target TaskScheduler (the parameterless overload of Start targets TaskScheduler.Current).  When you construct a Task with one of Task’s constructors, the Task is inactive: it has not been given to any scheduler yet, and thus there’s nothing to actually execute it.  If you never Start a Task, it’ll never be queued, and so it’ll never complete.  To get the Task to execute, it needs to be queued to a scheduler, so that the scheduler can execute it when and where the scheduler sees fit to do so.  The act of calling Start on a Task will twiddle some bits in the Task (e.g. changing its state from Created to WaitingToRun) and will then pass the Task to the target scheduler via the TaskScheduler’s QueueTask method.  At that point, the task’s future execution is in the hands of the scheduler, which should eventually execute the Task via the TaskScheduler’s TryExecuteTask method.

    Task.Factory.StartNew is shorthand for new’ing up a Task and Start’ing it.  So, the following code:

    var t = Task.Factory.StartNew(someDelegate);

    is functionally equivalent to:

    var t = new Task(someDelegate); 
    t.Start();

    Performance-wise, the former is slightly more efficient.  As mentioned in response to question #3, Start employs synchronization to ensure that the Task instance on which Start is being called hasn’t already been started, or isn’t concurrently being started.  In contrast, the implementation of StartNew knows that no one else could be starting the Task concurrently, as it hasn’t given out that reference to anyone… so StartNew doesn’t need to employ that synchronization.

    If you want the Task to get a reference to itself.  Consider the following (buggy) code:

    Task theTask = null; 
    theTask = Task.Run(() => Console.WriteLine(“My ID is {0}.”, theTask.Id));

    Spot the flaw?  There’s a race.  During the call to Task.Run, a new Task object is created and is queued to the ThreadPool scheduler.  If there’s not that much going on in the ThreadPool, a thread from the pool might pick it up almost instantly and start running it.  That thread is now racing to access the variable ‘theTask’ with the main thread that called Task.Run and that needs to store the created Task into that ‘theTask’ variable.  I can fix this race by separating the construction and scheduling:

    Task theTask = null; 
    theTask = new Task(() =>Console.WriteLine(“My ID is {0}.”, theTask.Id)); 
    theTask.Start(TaskScheduler.Default);

    Now I’m now sure that the Task instance will have been stored into the ‘theTask’ variable before the ThreadPool processes the Task, because the ThreadPool won’t even get a reference to the Task until Start is called to queue it, and by that point, the reference has already been set.

    References: http://blogs.msdn.com/b/pfxteam/archive/2012/01/14/10256832.aspx

    Rachit


    Please mark as answer or vote as helpful if my reply does

    • Marked as answer by Mou_kolkata Friday, April 17, 2015 7:46 AM
    Friday, April 17, 2015 2:32 AM

All replies

  • The first example allows you to create a task without starting it for cases where you may want to do that.

    For example 2 and 3, it's explained here http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx

    Friday, April 17, 2015 12:44 AM
  • The Start instance method may be used if and only if the Task is in the Created state (i.e. Task.Status returns TaskStatus.Created).  And the only way a Task can be in the Created state is if the Task were instantiated using one of Task’s public constructors, e.g. "var t = new Task(someDelegate);”.

    It queues the Task to the target TaskScheduler (the parameterless overload of Start targets TaskScheduler.Current).  When you construct a Task with one of Task’s constructors, the Task is inactive: it has not been given to any scheduler yet, and thus there’s nothing to actually execute it.  If you never Start a Task, it’ll never be queued, and so it’ll never complete.  To get the Task to execute, it needs to be queued to a scheduler, so that the scheduler can execute it when and where the scheduler sees fit to do so.  The act of calling Start on a Task will twiddle some bits in the Task (e.g. changing its state from Created to WaitingToRun) and will then pass the Task to the target scheduler via the TaskScheduler’s QueueTask method.  At that point, the task’s future execution is in the hands of the scheduler, which should eventually execute the Task via the TaskScheduler’s TryExecuteTask method.

    Task.Factory.StartNew is shorthand for new’ing up a Task and Start’ing it.  So, the following code:

    var t = Task.Factory.StartNew(someDelegate);

    is functionally equivalent to:

    var t = new Task(someDelegate); 
    t.Start();

    Performance-wise, the former is slightly more efficient.  As mentioned in response to question #3, Start employs synchronization to ensure that the Task instance on which Start is being called hasn’t already been started, or isn’t concurrently being started.  In contrast, the implementation of StartNew knows that no one else could be starting the Task concurrently, as it hasn’t given out that reference to anyone… so StartNew doesn’t need to employ that synchronization.

    If you want the Task to get a reference to itself.  Consider the following (buggy) code:

    Task theTask = null; 
    theTask = Task.Run(() => Console.WriteLine(“My ID is {0}.”, theTask.Id));

    Spot the flaw?  There’s a race.  During the call to Task.Run, a new Task object is created and is queued to the ThreadPool scheduler.  If there’s not that much going on in the ThreadPool, a thread from the pool might pick it up almost instantly and start running it.  That thread is now racing to access the variable ‘theTask’ with the main thread that called Task.Run and that needs to store the created Task into that ‘theTask’ variable.  I can fix this race by separating the construction and scheduling:

    Task theTask = null; 
    theTask = new Task(() =>Console.WriteLine(“My ID is {0}.”, theTask.Id)); 
    theTask.Start(TaskScheduler.Default);

    Now I’m now sure that the Task instance will have been stored into the ‘theTask’ variable before the ThreadPool processes the Task, because the ThreadPool won’t even get a reference to the Task until Start is called to queue it, and by that point, the reference has already been set.

    References: http://blogs.msdn.com/b/pfxteam/archive/2012/01/14/10256832.aspx

    Rachit


    Please mark as answer or vote as helpful if my reply does

    • Marked as answer by Mou_kolkata Friday, April 17, 2015 7:46 AM
    Friday, April 17, 2015 2:32 AM