Created
November 26, 2025 07:56
-
-
Save devops-school/fd0f12f77a81de2384f6bade64598e0c to your computer and use it in GitHub Desktop.
DOTNET: Threading Design Example threading anti-patternsm, proper async/await & concurrency behavior
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System.Diagnostics; | |
| using System.Threading; | |
| using System.Threading.Tasks; | |
| // Minimal API demo to show bad vs good threading practices in .NET | |
| var builder = WebApplication.CreateBuilder(args); | |
| var app = builder.Build(); | |
| // Simple concurrency metrics | |
| int currentBad = 0, maxBad = 0; | |
| int currentGood = 0, maxGood = 0; | |
| app.MapGet("/", () => | |
| "ThreadingDemo running. Use /bad, /good, /stats. Load-test /bad vs /good to see differences." | |
| ); | |
| // ❌ BAD ENDPOINT: blocking, sync-over-async, Thread.Sleep | |
| app.MapGet("/bad", () => | |
| { | |
| var concurrent = Interlocked.Increment(ref currentBad); | |
| UpdateMax(ref maxBad, concurrent); | |
| var sw = Stopwatch.StartNew(); | |
| try | |
| { | |
| // Simulate some blocking CPU work | |
| Thread.Sleep(20); // blocks the thread | |
| // Simulate I/O done in a bad way: sync-over-async | |
| for (int i = 0; i < 10; i++) | |
| { | |
| // BAD: This blocks the thread while waiting for an async operation | |
| Task.Delay(100).GetAwaiter().GetResult(); | |
| } | |
| sw.Stop(); | |
| return Results.Text( | |
| $"BAD handled in {sw.ElapsedMilliseconds} ms; " + | |
| $"current BAD concurrency = {concurrent}" | |
| ); | |
| } | |
| finally | |
| { | |
| Interlocked.Decrement(ref currentBad); | |
| } | |
| }); | |
| // ✅ GOOD ENDPOINT: proper async/await, no blocking | |
| app.MapGet("/good", async () => | |
| { | |
| var concurrent = Interlocked.Increment(ref currentGood); | |
| UpdateMax(ref maxGood, concurrent); | |
| var sw = Stopwatch.StartNew(); | |
| try | |
| { | |
| // Async-friendly "work" - yields the thread | |
| await Task.Delay(20); | |
| // Same simulated I/O, but correctly awaited | |
| for (int i = 0; i < 10; i++) | |
| { | |
| await Task.Delay(100); | |
| } | |
| sw.Stop(); | |
| return Results.Text( | |
| $"GOOD handled in {sw.ElapsedMilliseconds} ms; " + | |
| $"current GOOD concurrency = {concurrent}" | |
| ); | |
| } | |
| finally | |
| { | |
| Interlocked.Decrement(ref currentGood); | |
| } | |
| }); | |
| // Expose simple stats so you can see concurrency behavior | |
| app.MapGet("/stats", () => | |
| { | |
| return Results.Json(new | |
| { | |
| currentBad, | |
| maxBad, | |
| currentGood, | |
| maxGood | |
| }); | |
| }); | |
| app.Run(); | |
| static void UpdateMax(ref int max, int value) | |
| { | |
| int initial; | |
| do | |
| { | |
| initial = max; | |
| if (value <= initial) | |
| break; | |
| } while (Interlocked.CompareExchange(ref max, value, initial) != initial); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment