Joydip Kanjilal
Contributor

On Task.Factory.StartNew and Task.Run methods

opinion
Apr 22, 20165 mins
Software Development

Learn the best practices to create and schedule tasks efficiently in .Net using Task.Run and Task.Factory.StartNew methods

When creating tasks using Task.Factory.StartNew or Task.Run methods, you should keep certain important points in mind when writing asynchronous code. In most cases, it is advisable to avoid using Task.Factory.StartNew method if you are working with asynchronous code. If you are working with parallel code, I would say that StartNew is a good choice.

A task scheduler is a component that is responsible for scheduling tasks; The .Net framework provides you with two task schedulers. There’s the default task scheduler that runs on the .Net framework thread pool, and there’s the task scheduler that executes on the synchronization context of a specified target. The default task scheduler will suffice most of the time, but you can also build your own custom task scheduler to provide added functionalities. To build your own custom task scheduler you would need to create a class that extends the System.Threading.Tasks.TaskScheduler class.

How do I create Tasks using the Task Parallel Library?

There are several ways in which you can create and start tasks in .Net. You need to make use of the System.Threading.Tasks.Task or System.Threading.Tasks.Task class to create tasks (a schedulable unit of work). While the former is used to create a task that doesn’t return a value, the latter is used to create tasks that have return values. The Task.Factory property is an instance of the TaskFactory class. This property is used to create and schedule tasks. While the Task.Factory.StartNew method works like a fork operation and is used to create and start new tasks, the Wait method works just like a join operation and waits for the task to be complete.

The following code snippet illustrates how you can use the Task.Factory.StartNew method.

Task.Factory.StartNew(() => TestMethod(), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);

You can also create a Task using the Task.Run method as shown in the code snippet below.

public async Task DoSomeWork()

        {

            await Task.Run(() => TestMethod());

        }

        void TestMethod()

        {

            Console.WriteLine("Hello world!");

        }

If you would like to return a value from a Task you can take advantage of Task.FromResult method as shown in the code snippet below.

public async Task DoSomeWork()

   {

      string text = await Task.FromResult<string>(GetMessage());

   }

private string GetMessage()

   {

      return "Hello world!";

   }

You can also create tasks using a delegate or an action. The following code snippet shows how you can create tasks using actions and delegates.

Task task1 = new Task (new Action(Display));

task1.Start();

Task task2 = new Task (delegate { Display(); });

task2.Start();

You can also create tasks using lamba and anonymous methods.

Task.Factory.StartNew and Task.Run

Task.Factory.StartNew is a quick way of creating and starting a Task. Note that a call to Task.Factory.StartNew is functionally equivalent to creating a task instance and then calling the Start method on the instance. However, it is not recommended to be used for reasons aplenty. If you would like to execute synchronous code, Task.Factory.StartNew is not a good choice.

Note that if a task scheduler is available, the StartNew method will execute the task on that task scheduler. On the contrary, if a scheduler isn’t available, it would execute the task on a thread pool thread. It should be noted that Task.Factory.StartNew defaults to TaskScheduler.Current and not TaskScheduler.Default.

Note that a call to Task.Run(action) is equivalent to the following statement: Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

On the contrary, a call to Task.Factory.StartNew(action) is equivalent to the following statement: Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Current);

If may want to use Task.Factory.StartNew if you have created a custom task scheduler and pass the scheduler instance to it explicitly. I would always recommend using Task.Run as it is much simpler and has safer defaults. In other words, we should avoid using Task.Factory.StartNew unless there is a need to create a task scheduler and then pass it explicitly when calling the StartNew method to create a new task and schedule it. If you were to use the TaskFactory.StartNew method effectively and reliably, you should use a custom task scheduler and then specify the CancellationToken and TaskCreationOptions.

The Task.Run method is recommended to use when you don’t need to have much fine-grained control over thread scheduling and its intricacies. You should use Task.Run primarily on CPU bound methods. However, you should use Task.Run while invoking the task and not inside the implementation of the task. In other words, you should use Task.Run not inside any implementation of a method but at the point where the method is called. As an example, the following code snippet is an example of a “bad” piece of code.

public async Task<string> DownloadDataFromWebAsync(Uri uri)

        {

            return await Task.Run(() =>

            {

                using (WebClient webClient = new WebClient())

                {

                    return webClient.DownloadString(uri);

                }

            });

        }

Refer to the code snippet given above. The method isn’t scalable as it would block the background thread, retrieve a thread from the thread pool and execute synchronously on it. Hence, it would consume more resources in your system.

Joydip Kanjilal
Contributor

Joydip Kanjilal is a Microsoft Most Valuable Professional (MVP) in ASP.NET, as well as a speaker and the author of several books and articles. He received the prestigious MVP award for 2007, 2008, 2009, 2010, 2011, and 2012.

He has more than 20 years of experience in IT, with more than 16 years in Microsoft .Net and related technologies. He has been selected as MSDN Featured Developer of the Fortnight (MSDN) and as Community Credit Winner several times.

He is the author of eight books and more than 500 articles. Many of his articles have been featured at Microsoft’s Official Site on ASP.Net.

He was a speaker at the Spark IT 2010 event and at the Dr. Dobb’s Conference 2014 in Bangalore. He has also worked as a judge for the Jolt Awards at Dr. Dobb's Journal. He is a regular speaker at the SSWUG Virtual Conference, which is held twice each year.

More from this author