4

Is the code from this page really the best way to simulate a long running task?

The console app I've worked up is fairly simple and seems to work just fine.

I'm not sure if I could swap out DoExpensiveCalculation for an an Async method, like GetStringAsync from HttpClient without issue.

using System;
using System.Threading.Tasks;

namespace ExpensiveAsync
{
    public class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("started");

            var t = CalculateResult("stuff and that");

            Console.WriteLine("press return");
            Console.ReadLine();
        }

        public static async Task<string> CalculateResult(string data)
        {
            // This queues up the work on the threadpool.
            var expensiveResultTask = Task.Run(() => DoExpensiveCalculation(data));

            // Note that at this point, you can do some other work concurrently,
            // as CalculateResult() is still executing!

            Console.WriteLine("concurrent");

            // Execution of CalculateResult is yielded here!
            var result = await expensiveResultTask; // CalculateResult returns the Task object here

            Console.WriteLine("synchronous"); // this code runs once the DoExpensiveCalculation method has finished

            return result;
        }

        public static string DoExpensiveCalculation(string data)
        {
            var completionTime = DateTime.Now.AddSeconds(3);

            Console.WriteLine("begin");

            while (DateTime.Now < completionTime) ;

            Console.WriteLine("finish");

            return data;
        }
    }
}
Matt W
  • 11,753
  • 25
  • 118
  • 215
  • 2
    It depends on what you mean by "expensive". If you mean "takes some time to complete", you can just do `Task.Delay(15_000)` (15 seconds, adjust as needed). If you actually need it to consume cpu while it is running then you need to implement a busy wait loop that just keeps looping until enough time has elapsed, you can wrap such a loop in a task and use `Stopwatch` to figure out when to complete. – Lasse V. Karlsen Jan 23 '19 at 15:52
  • I had tried that with Thread.Sleep(), but that seems to sleep the calling code as well. What would be the critical difference between that and Task.Delay() (which I'm obviously not getting)? – Matt W Jan 23 '19 at 16:16
  • https://stackoverflow.com/questions/20082221/when-to-use-task-delay-when-to-use-thread-sleep – derpirscher Jan 23 '19 at 18:07
  • To observe how `async-await` works (I assume this is what you are trying to achieve by "logging" into console), make `Main` method asynchronous as well. Because in current implementation your application exits(prints: "press return") before task is complete – Fabio Jan 23 '19 at 21:53

1 Answers1

4

The structure of the code implies that DoExpensiveCalculation is a CPU-bound operation. If you want to stub out a CPU-bound operation without taking up CPU, Thread.Sleep is the appropriate option.

I'm not sure if I could swap out DoExpensiveCalculation for an an Async method, like GetStringAsync from HttpClient without issue.

Well, that's completely different. GetStringAsync is not a CPU-bound operation; it's an I/O-bound operation.

I/O-bound operations are naturally asynchronous, and they can be used directly with async and await. CPU-bound operations are naturally synchronous, so that's where you sometimes want to use Task.Run. "Sometimes" in this case generally means "when you're on a UI thread", so that the UI isn't frozen while the CPU-bound operation is running. When you wrap the CPU-bound operation in a Task.Run, you can then treat it as asynchronous, i.e., awaiting it from the UI thread.

Your example code is a console app, where the use of Task.Run is usually not necessary. Console apps generally run whatever code they need to run and exit; in most cases there's no UI that needs to be kept responsive, and thus no need for Task.Run.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810