{"id":54210,"date":"2025-11-26T07:56:55","date_gmt":"2025-11-26T07:56:55","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=54210"},"modified":"2025-11-26T07:56:55","modified_gmt":"2025-11-26T07:56:55","slug":"dotnet-threading-design-example-threading-anti-patternsm-proper-async-await-concurrency-behavior","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/dotnet-threading-design-example-threading-anti-patternsm-proper-async-await-concurrency-behavior\/","title":{"rendered":"DOTNET: Threading Design Example threading anti-patternsm, proper async\/await &amp; concurrency behavior"},"content":{"rendered":"\n<p><br>We can do this with <strong>one self-contained minimal API<\/strong> that has:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A <strong>\u201cbad\u201d endpoint<\/strong> showing <strong>threading anti-patterns<\/strong> (blocking, <code>.Result<\/code>, <code>Thread.Sleep<\/code>)<\/li>\n\n\n\n<li>A <strong>\u201cgood\u201d endpoint<\/strong> using <strong>proper async\/await<\/strong><\/li>\n\n\n\n<li>A <strong>\/stats<\/strong> endpoint to see concurrency behavior<\/li>\n\n\n\n<li>Then we\u2019ll use <strong>dotnet-counters<\/strong> + simple load to <em>see<\/em> the impact.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">1\ufe0f\u20e3 Create the project<\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">dotnet <span class=\"hljs-keyword\">new<\/span> web -n ThreadingDemo\ncd ThreadingDemo\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Replace <strong>all contents<\/strong> of <code>Program.cs<\/code> with this:<\/p>\n\n\n\n<script src=\"https:\/\/gist.github.com\/devops-school\/fd0f12f77a81de2384f6bade64598e0c.js\"><\/script>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\"><\/code><\/span><\/pre>\n\n\n<p>This single file gives you:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>\/bad<\/code> \u2192 <strong>blocked threads \/ ThreadPool pressure<\/strong><\/li>\n\n\n\n<li><code>\/good<\/code> \u2192 <strong>healthy async behavior<\/strong><\/li>\n\n\n\n<li><code>\/stats<\/code> \u2192 concurrency snapshot<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2\ufe0f\u20e3 Run the app<\/h2>\n\n\n\n<p>From the <code>ThreadingDemo<\/code> folder:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">dotnet run\n<\/code><\/span><\/pre>\n\n\n<p>It will listen on:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>http:\/\/localhost:5000<\/code> (default)<\/li>\n<\/ul>\n\n\n\n<p>Test quickly:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">curl http:<span class=\"hljs-comment\">\/\/localhost:5000\/bad<\/span>\ncurl http:<span class=\"hljs-comment\">\/\/localhost:5000\/good<\/span>\ncurl http:<span class=\"hljs-comment\">\/\/localhost:5000\/stats<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">3\ufe0f\u20e3 Experience the problem: BAD threading under load<\/h2>\n\n\n\n<p>The <strong>\/bad<\/strong> endpoint:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Uses <code>Thread.Sleep()<\/code> \u2192 blocks the thread<\/li>\n\n\n\n<li>Uses <code>Task.Delay(100).Result<\/code> \u2192 <strong>sync-over-async<\/strong>, blocks the thread<\/li>\n\n\n\n<li>Under concurrent load, the <strong>ThreadPool threads get stuck<\/strong>, new requests wait \u21d2 <strong>latency goes up<\/strong>, throughput drops.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83e\uddea Simulate load on \/bad<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">Option A \u2013 PowerShell (parallel-ish)<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\"># Fire 50 requests, roughly in parallel<\/span>\n<span class=\"hljs-number\">1.<\/span><span class=\"hljs-number\">.50<\/span> | <span class=\"hljs-keyword\">ForEach<\/span>-Object {\n    Start-Job { curl <span class=\"hljs-string\">\"http:\/\/localhost:5000\/bad\"<\/span> }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Or more aggressively:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-number\">1.<\/span><span class=\"hljs-number\">.200<\/span> | ForEach-<span class=\"hljs-built_in\">Object<\/span> {\n    Start-Job { curl <span class=\"hljs-string\">\"http:\/\/localhost:5000\/bad\"<\/span> }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Then check:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">curl <span class=\"hljs-string\">\"http:\/\/localhost:5000\/stats\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You\u2019ll see:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>maxBad<\/code> grow<\/li>\n\n\n\n<li>Responses from <code>\/bad<\/code> will be <strong>slow<\/strong> (hundreds or thousands of ms)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">4\ufe0f\u20e3 Compare with GOOD endpoint under same load<\/h2>\n\n\n\n<p>Do the same with <code>\/good<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-number\">1.<\/span><span class=\"hljs-number\">.200<\/span> | ForEach-<span class=\"hljs-built_in\">Object<\/span> {\n    Start-Job { curl <span class=\"hljs-string\">\"http:\/\/localhost:5000\/good\"<\/span> }\n}\ncurl <span class=\"hljs-string\">\"http:\/\/localhost:5000\/stats\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You should observe:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>GOOD handled in ... ms<\/code> is more stable<\/li>\n\n\n\n<li><code>maxGood<\/code> may be higher (more concurrency successfully handled)<\/li>\n\n\n\n<li>Latency is smoother because threads are <strong>not blocked<\/strong> \u2014 they\u2019re freed while awaiting.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">5\ufe0f\u20e3 Debug \/ Observe Threading Issues with Tools<\/h2>\n\n\n\n<p>Now let\u2019s add <strong>tools<\/strong> on top, so you can show this in training.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd39 Step 1: Find the process ID<\/h3>\n\n\n\n<p>In a new terminal:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">dotnet-counters ps\n<\/code><\/span><\/pre>\n\n\n<p>Look for <code>ThreadingDemo<\/code> \/ <code>dotnet<\/code> with the project path.<\/p>\n\n\n\n<p>Note the <strong>PID<\/strong> (e.g., 12345).<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd39 Step 2: Monitor runtime with <code>dotnet-counters<\/code><\/h3>\n\n\n\n<p>Run:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">dotnet-counters<\/span> <span class=\"hljs-selector-tag\">monitor<\/span> <span class=\"hljs-selector-tag\">--process-id<\/span> &lt;<span class=\"hljs-selector-tag\">PID<\/span>&gt; <span class=\"hljs-selector-tag\">System<\/span><span class=\"hljs-selector-class\">.Runtime<\/span> <span class=\"hljs-selector-tag\">Microsoft<\/span><span class=\"hljs-selector-class\">.AspNetCore<\/span><span class=\"hljs-selector-class\">.Hosting<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Watch these metrics while hitting <code>\/bad<\/code> vs <code>\/good<\/code>:<\/p>\n\n\n\n<p>Key ones:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>ThreadPool Thread Count<\/code><\/li>\n\n\n\n<li><code>ThreadPool Queue Length<\/code><\/li>\n\n\n\n<li><code>CPU Usage<\/code><\/li>\n\n\n\n<li><code>Requests \/ sec<\/code> (from hosting)<\/li>\n\n\n\n<li><code>gc-heap-size<\/code><\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">What you should see<\/h4>\n\n\n\n<p><strong>When hammering <code>\/bad<\/code>:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>ThreadPool <strong>Thread Count<\/strong> goes up<\/li>\n\n\n\n<li>ThreadPool <strong>Queue Length<\/strong> might stay elevated<\/li>\n\n\n\n<li>CPU can be high due to lots of blocking and context switching<\/li>\n\n\n\n<li>Requests\/sec typically <strong>lower<\/strong> than expected<\/li>\n<\/ul>\n\n\n\n<p><strong>When hammering <code>\/good<\/code>:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>ThreadPool threads are reused efficiently<\/li>\n\n\n\n<li>Queue Length often stays low<\/li>\n\n\n\n<li>CPU usage is better for same number of requests<\/li>\n\n\n\n<li>Requests\/sec improves, latency is lower<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd39 Step 3: Visual Studio Diagnostic Tools (optional)<\/h3>\n\n\n\n<p>If you run from Visual Studio:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Start the app with <strong>Debug \u2192 Start Debugging<\/strong>.<\/li>\n\n\n\n<li>Open <strong>Debug \u2192 Windows \u2192 Parallel Stacks \/ Parallel Tasks<\/strong>.<\/li>\n\n\n\n<li>Watch the number of running threads and tasks as you hammer <code>\/bad<\/code> and <code>\/good<\/code>.<\/li>\n<\/ol>\n\n\n\n<p>You\u2019ll see:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>For <code>\/bad<\/code>: more stuck threads, longer lifetimes<\/li>\n\n\n\n<li>For <code>\/good<\/code>: tasks start\/complete quickly, threads not held hostage<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">6\ufe0f\u20e3 How to explain this behavior conceptually<\/h2>\n\n\n\n<p><strong>In <code>\/bad<\/code>:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>Thread.Sleep<\/code> \u2192 the worker thread is doing <em>nothing<\/em> but cannot process other requests.<\/li>\n\n\n\n<li><code>Task.Delay(...).Result<\/code> \u2192 the operation is asynchronous internally, but you force it to be synchronous, so:\n<ul class=\"wp-block-list\">\n<li>The thread blocks until delay finishes<\/li>\n\n\n\n<li>Under load: too many blocked threads \u2192 ThreadPool grows \u2192 context switching overhead \u2192 queue length and latency grow.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><strong>In <code>\/good<\/code>:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>await Task.Delay(...)<\/code> \u2192 the thread returns to the pool while waiting<\/li>\n\n\n\n<li>When the delay completes, the continuation resumes (on a thread pool worker)<\/li>\n\n\n\n<li>The same set of threads can handle many more in-flight requests.<\/li>\n<\/ul>\n\n\n\n<p>So <strong>it\u2019s mostly a programming practice issue<\/strong>, not a \u201c.NET design flaw\u201d:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u274c Bad code: blocking, sync-over-async, Thread.Sleep on server<\/li>\n\n\n\n<li>\u2705 Good code: async all the way, no <code>.Result<\/code>, no <code>.Wait()<\/code><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">7\ufe0f\u20e3 Quick summary for your training slide<\/h2>\n\n\n\n<p>You can summarize this demo as:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<ul class=\"wp-block-list\">\n<li><code>\/bad<\/code>: Blocking calls (<code>Thread.Sleep<\/code>, <code>.Result<\/code>) \u2192 ThreadPool starvation \u2192 high latency, low throughput<\/li>\n\n\n\n<li><code>\/good<\/code>: Proper async\/await \u2192 threads freed \u2192 better scalability and responsiveness<\/li>\n\n\n\n<li>Tools: <code>dotnet-counters<\/code> + <code>\/stats<\/code> endpoint give direct visibility into concurrency &amp; runtime behavior.<\/li>\n<\/ul>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>We can do this with one self-contained minimal API that has: 1\ufe0f\u20e3 Create the project Replace all contents of Program.cs with this: This single file gives you: 2\ufe0f\u20e3 Run the&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[11138],"tags":[],"class_list":["post-54210","post","type-post","status-publish","format-standard","hentry","category-best-tools"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54210","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=54210"}],"version-history":[{"count":1,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54210\/revisions"}],"predecessor-version":[{"id":54211,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54210\/revisions\/54211"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=54210"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=54210"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=54210"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}