From 4c5328284771659085783eac3bd4d8db70adb04c Mon Sep 17 00:00:00 2001 From: Noah Falk Date: Wed, 27 Apr 2022 07:21:19 -0700 Subject: [PATCH] Add threadpool starvation endpoints (#5113) We needed to demonstrate threadpool starvation for the tutorial I am adding. https://github.com/dotnet/diagnostics/issues/2907 --- .../Controllers/DiagnosticScenarios.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/core/diagnostics/DiagnosticScenarios/Controllers/DiagnosticScenarios.cs b/core/diagnostics/DiagnosticScenarios/Controllers/DiagnosticScenarios.cs index f3f7a32c8ad..1d587317e14 100644 --- a/core/diagnostics/DiagnosticScenarios/Controllers/DiagnosticScenarios.cs +++ b/core/diagnostics/DiagnosticScenarios/Controllers/DiagnosticScenarios.cs @@ -132,6 +132,59 @@ public ActionResult highcpu(int milliseconds) return "success:highcpu"; } + + + [HttpGet] + [Route("taskwait")] + public ActionResult TaskWait() + { + // Using Task.Wait() or Task.Result causes the current thread to block until the + // result has been computed. This is the most common cause of threadpool starvation + // and NOT recommended in your own code unless you know the task is complete and won't + // need to block. + Customer c = PretendQueryCustomerFromDbAsync("Dana").Result; + return "success:taskwait"; + } + + [HttpGet] + [Route("tasksleepwait")] + public ActionResult TaskSleepWait() + { + // Starting in .NET 6.0 the threadpool can recognize some of the common ways that + // code blocks on tasks completing and can mitigate it by more quickly + // scaling up the number of threadpool threads. This example is a less common + // way to block on a task completing to show what happens when the threadpool + // doesn't recognize the blocking behavior. This code is NOT recommended. + Task dbTask = PretendQueryCustomerFromDbAsync("Dana"); + while(!dbTask.IsCompleted) + { + Thread.Sleep(10); + } + return "success:tasksleepwait"; + } + + [HttpGet] + [Route("taskasyncwait")] + public async Task> TaskAsyncWait() + { + // Using the await keyword allows the current thread to service other workitems and + // when the database lookup Task is complete a thread from the threadpool will resume + // execution here. This way no thread is blocked and large numbers of requests can + // run in parallel without blocking + Customer c = await PretendQueryCustomerFromDbAsync("Dana"); + return "success:taskasyncwait"; + } + + + async Task PretendQueryCustomerFromDbAsync(string customerId) + { + // To keep the demo app easy to set up and performing consistently we have replaced a real database query + // with a fixed delay of 500ms. The impact on application performance should be similar to using a real + // database that had similar latency. + await Task.Delay(500); + return new Customer(customerId); + } + } class Customer