Middle · Trace · AsyncLocal flows through logical operations

Trace the three lines using the gate and execution-context boundaries, not thread timing.

Think through your answer before opening the explanation.

Runtime & concurrency

using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Reflection;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;

var local = new AsyncLocal<string?>();
local.Value = "parent";
var gate = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
Task child = Child();
Console.WriteLine($"parent-before:{local.Value}");
gate.SetResult(true);
await child;
Console.WriteLine($"parent-after:{local.Value}");
async Task Child() { local.Value = "child"; await gate.Task; Console.WriteLine($"child:{local.Value}"); }
Reveal answer and reasoning

The output is parent-before:parent, child:child, and parent-after:parent. Child sets its own ambient string and then waits on the incomplete gate. The caller resumes its parent context and prints before. Setting the gate allows the child's continuation to print its own value. Awaiting that owned task finishes before the parent's final observation, which still reads parent.

The ordering is controlled by the incomplete task and awaits, so no sleep or assumed worker thread is necessary. A common wrong trace makes parent-before or parent-after read child because it models AsyncLocal as a static global. Another prints the child line first without accounting for the gate. The example uses immutable strings and slot assignments, not shared mutable-object changes.

Its result therefore does not establish that every object reachable from ambient context is cloned or protected from cross-flow mutation.

What a strong answer covers

  • The gate prevents early child output.
  • Parent and child slot assignments remain distinct here.
  • No physical thread identity is asserted.

Common pitfall

Ignoring the gate or treating AsyncLocal assignment as one global mutable value.

Follow-up questions

  1. What if Child reads the inherited value before replacing it?
  2. What would mutating a shared object differ from replacing the slot?

Self-review

Choose what to revisit. These marks are your own assessment and are separate from lesson completion.

Primary references

Open lesson and code ↗

← All interview questions