Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -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"
}
25 changes: 17 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,30 @@ 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 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.
You can even start multiple requestor-clients in different terminals against different endpoints.

[Source code]
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
Expand All @@ -28,7 +37,7 @@ On the left side you can `dotnet run <method name>` where method name is one of

|dotnet run \<parameter> | 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!|
Expand Down
19 changes: 17 additions & 2 deletions Requestor/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Net.Http;
using System.Threading;


namespace Requestor
{
class Program
Expand All @@ -13,14 +14,27 @@ class Program
static string path = "hello";
static List<Thread> requestThreads = new List<Thread>();

// 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];
Console.WriteLine();
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))
{
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion Requestor/Requestor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>

</Project>
8 changes: 4 additions & 4 deletions Threading/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> 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<string> HelloAsync()
{
await Task.Delay(2000);
return "Hello World";
return "async-over-async";
}
}
}
3 changes: 2 additions & 1 deletion Threading/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Threading
{
Expand All @@ -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())
{
Expand Down
6 changes: 2 additions & 4 deletions Threading/Threading.csproj
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="6" />
</ItemGroup>
<!-- No explicit PackageReference needed for Microsoft.AspNetCore.App in .NET 9.0 -->

</Project>
Binary file added packages-microsoft-prod.deb
Binary file not shown.