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. Related content feature 14 great preprocessors for developers who love to code Sometimes it seems like the rules of programming are designed to make coding a chore. Here are 14 ways preprocessors can help make software development fun again. By Peter Wayner Nov 18, 2024 10 mins Development Tools Software Development feature Designing the APIs that accidentally power businesses Well-designed APIs, even those often-neglected internal APIs, make developers more productive and businesses more agile. By Jean Yang Nov 18, 2024 6 mins APIs Software Development news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks news Go language evolving for future hardware, AI workloads The Go team is working to adapt Go to large multicore systems, the latest hardware instructions, and the needs of developers of large-scale AI systems. By Paul Krill Nov 15, 2024 3 mins Google Go Generative AI Programming Languages Resources Videos