(everything below initially written for .Net 5.0 but now targeting .Net 6.0)
Consider this Asp.Net controller used as REST Api :
[AllowAnonymous]
[Route("[controller]")]
[ApiController]
[ApiConventionType(typeof(DefaultApiConventions))]
public class DebugController : ControllerBase {
private readonly ILoggingWrapper log;
public DebugController(ILoggingWrapper log) {
this.log = log;
log.Information("Constructor called");
}
private async Task Slow() {
await Task.Delay(15000).ConfigureAwait(false);
}
[Authorize()]
[HttpGet]
[Route("testSlow")]
public async Task<ActionResult> TestSlow() {
await Slow().ConfigureAwait(false);
return Ok();
}
[Authorize()]
[HttpGet]
[Route("testFast")]
public async Task<ActionResult> TestFast() {
return Ok();
}
}
I have a Swagger page configured, where I can call TestSlow and TestFast on demand.
Experience #1
- Open a browser tab with the Swagger page open,
- Call only
TestSlow
Result
The DebugController
constructor enters immediately, then TestSlow starts immediately and returns after 15 seconds
Experience #2
- Open a browser tab with the Swagger page open,
- Open another browser tab with the Swagger page open,
- On the first Swagger page, call only
TestSlow
- Quickly switch to the other Swagger page, call
TestFast
Result
- The
DebugController
constructor enters immediately, then TestSlow starts immediately - When calling
TestFast
, and whileTestslow
is still running, TheDebugController
constructor enters immediately and TestFast starts immediately.
the two experiences above behave as I expect it : Asp.Net is multithreaded and calling one endpoint does not stop another client from calling another endpoint, even if the first endpoint is still being served to the first client.
Experience #3 (the weird one)
- Open a browser tab with the Swagger page open,
- Open another browser tab with the Swagger page open,
- On the first Swagger page, call only
TestSlow
- Quickly switch to the other Swagger page, call
TestSlow
there too
Result
- The
DebugController
constructor enters immediately, thenTestSlow
starts immediately - When calling the other
TestSlow
, the Controller's contructor is not called before the first call to TestSlow is entirely finished and has returned!. In effect, the two calls to TestSlow happen sequentially.
Why is that? Why does multithreading suddenly seem to "disappear" the moment I try to call the same endpoint twice, even though I do it from two different clients?