From 7c09ddac81ef3f8f14309ec43a7f5dedd009912e Mon Sep 17 00:00:00 2001 From: Frederic Luchting Date: Fri, 12 Sep 2025 19:05:49 +0000 Subject: [PATCH 1/4] create devcontainer config file for dotnet 9 --- .devcontainer/devcontainer.json | 10 ++++++++++ packages-microsoft-prod.deb | Bin 0 -> 4288 bytes 2 files changed, 10 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100644 packages-microsoft-prod.deb diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..825734e --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,10 @@ +{ + "name": "ThreadStarvation Codespace", + "image": "mcr.microsoft.com/devcontainers/dotnet:9.0", + "features": { + "ghcr.io/devcontainers/features/dotnet:1": { + "version": "9.0" + } + }, + "postCreateCommand": "dotnet --version" +} diff --git a/packages-microsoft-prod.deb b/packages-microsoft-prod.deb new file mode 100644 index 0000000000000000000000000000000000000000..2ba9abcd332e48dfcc6ead79faef58cd8538919d GIT binary patch literal 4288 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 x Date: Fri, 12 Sep 2025 19:53:36 +0000 Subject: [PATCH 2/4] bump to dotnet 9.0 --- Requestor/Requestor.csproj | 2 +- Threading/Startup.cs | 3 ++- Threading/Threading.csproj | 6 ++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Requestor/Requestor.csproj b/Requestor/Requestor.csproj index 120e38c..92e46dd 100644 --- a/Requestor/Requestor.csproj +++ b/Requestor/Requestor.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net9.0 diff --git a/Threading/Startup.cs b/Threading/Startup.cs index c1be74e..816a2ab 100644 --- a/Threading/Startup.cs +++ b/Threading/Startup.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace Threading { @@ -12,7 +13,7 @@ public void ConfigureServices(IServiceCollection services) services.AddMvc(opt => opt.EnableEndpointRouting = false); } - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { diff --git a/Threading/Threading.csproj b/Threading/Threading.csproj index 1541571..8e2a1f9 100644 --- a/Threading/Threading.csproj +++ b/Threading/Threading.csproj @@ -1,15 +1,13 @@ - net7.0 + net9.0 - - - + From 32fc06ed14ec5ed148b7eba715fdf1e548aedb1a Mon Sep 17 00:00:00 2001 From: Frederic Luchting Date: Fri, 12 Sep 2025 21:18:18 +0000 Subject: [PATCH 3/4] cool --- README.md | 21 +++++++++++++++------ Requestor/Program.cs | 19 +++++++++++++++++-- Threading/Controllers/HomeController.cs | 8 ++++---- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9858a53..b30ae1d 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,20 @@ This repository comes from a good presentation of Damian Edwards and David Fowle Simple story: -- One project is a little Hello world web application with 4 methods - - Good old full synchronous execution, works but not very efficient - - Modern app with legacy services: async-over-sync, not best but works - - Old app code using modern apis: sync-over-async, shows dead IIS (☠) because of thread starvation - - Full async, shows unlimited web scale +## Threading + +A little Hello world backend/web/app/server/ with 4 endpoints + +- Good old full synchronous execution down the line, works but not very efficient **sync-over-sync** +- Modern app with legacy services: Not best but works at least not worse than before **async-over-sync** +- Old code using modern apis: Shows dead lock in backend ☠ (IIS , kestrel, ngix) because of thread starvation **sync-over-async (☠)** +- Full async, shows unlimited web scale **full-async** + +## Requestor + +Makes as may requests against an enpoint as you like! +Just press key arrow up/down for parallel request threads. +They can simulate multiple clients. [Source code] https://github.com/davidfowl/NdcLondon2018 @@ -28,7 +37,7 @@ On the left side you can `dotnet run ` where method name is one of |dotnet run \ | comment| |---------|---| -|hello | good old sync | +|hello | good old sync-over-sync | |hello-async-over-sync | modern controller over old services | |hello-sync-over-async | ☠ can kill the server, must close terminal | |hello-async | scales!| diff --git a/Requestor/Program.cs b/Requestor/Program.cs index ad25887..cb2354f 100644 --- a/Requestor/Program.cs +++ b/Requestor/Program.cs @@ -5,6 +5,7 @@ using System.Net.Http; using System.Threading; + namespace Requestor { class Program @@ -13,6 +14,19 @@ class Program static string path = "hello"; static List requestThreads = new List(); + // Maps long response strings to short codes for console output + static string ShortenResponse(string input) + { + return input switch + { + "sync-over-sync" => "sos", + "async-over-sync" => "aos", + "sync-over-async (☠)" => "soa ☠", + "async-over-async" => "aoa*", + _ => input + }; + } + static void Main(string[] args) { if (args.Length > 0 && !string.IsNullOrEmpty(args[0])) path = args[0]; @@ -20,7 +34,7 @@ static void Main(string[] args) Console.WriteLine("--------------------------------"); Console.WriteLine($"Hammering endpoint: /{path}"); Console.WriteLine("Press ESC to stop, up- down- keys to change request count"); - http.Timeout = TimeSpan.FromMilliseconds(2700); + http.Timeout = TimeSpan.FromMilliseconds(3000); while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape)) { @@ -60,7 +74,8 @@ static void RequestAndLogLoopAsync() var sw = Stopwatch.StartNew(); var res = http.GetAsync($"http://localhost:5000/{path}").Result; var cnt = res.Content.ReadAsStringAsync().Result; - Console.Write($"{sw.ElapsedMilliseconds}, "); + + Console.Write($"{sw.ElapsedMilliseconds}-{ShortenResponse(cnt)}, "); } } catch (ThreadInterruptedException) diff --git a/Threading/Controllers/HomeController.cs b/Threading/Controllers/HomeController.cs index 7588941..8a32f34 100644 --- a/Threading/Controllers/HomeController.cs +++ b/Threading/Controllers/HomeController.cs @@ -10,28 +10,28 @@ public class HomeController : Controller public string Hello() { Thread.Sleep(2000); // DB query, REST call, - return "Hello World"; + return "sync-over-sync"; } [HttpGet("/hello-async-over-sync")] // New project using old services public async Task HelloAsyncOverSync() { await Task.Run(() => Thread.Sleep(2000)); - return "Hello World"; + return "async-over-sync"; } [HttpGet("/hello-sync-over-async")] // Old code using new APIs public string HelloSyncOverAsync() { Task.Delay(2000).Wait(); - return "Hello World"; + return "sync-over-async (☠)"; } [HttpGet("/hello-async")] // All new public async Task HelloAsync() { await Task.Delay(2000); - return "Hello World"; + return "async-over-async"; } } } From 0f306c28680f8a4e50b6eb119e1fa0467e9e7761 Mon Sep 17 00:00:00 2001 From: Frederic Luchting Date: Fri, 12 Sep 2025 22:38:45 +0000 Subject: [PATCH 4/4] Fix typos and improve clarity in README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b30ae1d..ec90184 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,14 @@ A little Hello world backend/web/app/server/ with 4 endpoints - Good old full synchronous execution down the line, works but not very efficient **sync-over-sync** - Modern app with legacy services: Not best but works at least not worse than before **async-over-sync** -- Old code using modern apis: Shows dead lock in backend ☠ (IIS , kestrel, ngix) because of thread starvation **sync-over-async (☠)** -- Full async, shows unlimited web scale **full-async** +- Old code using modern apis runs into dead locks in backend ☠ (IIS , kestrel, ngix) because of thread starvation :-( **sync-over-async (☠)** +- Full async, unlimited web scale **async-over-async** (:-)) ## Requestor Makes as may requests against an enpoint as you like! Just press key arrow up/down for parallel request threads. -They can simulate multiple clients. +You can even start multiple requestor-clients in different terminals against different endpoints. [Source code] https://github.com/davidfowl/NdcLondon2018 @@ -25,9 +25,9 @@ https://github.com/davidfowl/NdcLondon2018 [Youtube] https://www.youtube.com/watch?v=RYI0DHoIVaA -## Howto use it +## Howto to run it -- Open the repository folder in VS Code +- Open the repository folder in VS Code (or GitHub Codespace) - Open two terminal windows (CTRL+`) - Navigate in the left to ~/Requestor - Navigate in the right to ~/Threading