Interview Handbook by Sayed Elmahdy English

المحور 04 · C# و .NET

Async و Multithreading

Thread و Task، و async و await من جوّه، و WhenAll، و Cancellation، و locks، و race conditions، و deadlocks.

17 سؤالJunior · 3Mid · 11Senior · 38 اختيارات

المفاهيم

هنفهم إيه؟ بتستنى database أو خدمة دفع: إزاي تسيب التطبيق يخدم طلبات تانية أثناء الانتظار، وإزاي تمنع تعارض شغل متزامن؟ هنفرّق بين async وparallelism، ونستخدم الإلغاء والمزامنة لما يكون فيه سبب حقيقي.

قبل ما تبدأ: التنفيذ المتزامن يمشي خطوة ثم ينتظر انتهاءها قبل الخطوة التالية. في C#، Task بيمثل عملية أو نتيجة قد تكتمل لاحقاً؛ وجوده لوحده مش معناه thread جديد. ابدأ بكارت المطعم، وبعده افهم Task قبل وlock.

المصطلحات اللي تحتها خط منقّط تقدر تضغط عليها لمعرفة معناها، وترجع تكمل من نفس مكانك.

#Sync و Async: مثال الجرسون في المطعمSync vs async: the restaurant analogy

الجرسون (waiter) الـ Sync: بياخد الأوردر، ويروح المطبخ، ويقف مستني لحد ما الأكل يخلص، وبعدين يرجع. وطول الوقت ده مش بيخدم حد تاني. فلو عندك 10 زباين، محتاج 10 جرسونات.

الجرسون الـ Async: بياخد الأوردر، ويديه للمطبخ، ويروح يخدم ترابيزة تانية. ولما الأكل يجهز، المطبخ بينادي، وأي جرسون فاضي يوصّله. فجرسون أو اتنين يخدموا 10 ترابيزات.

الجرسون هو الـ thread، والمطبخ هو الـ (database، و ، و files). والـ async مش بيخلّي المطبخ أسرع، بيخلّي الجرسونات مايضيعوش وقتهم مستنيين.

❌ Sync: الـ thread واقف مستني
public IActionResult Get(int id)
{
    var order = db.Orders.Find(id);           // thread blocked ~20 ms
    var rate = http.GetStringAsync(ratesUrl).GetAwaiter().GetResult(); // blocks
    return Ok(order);
}
✅ Async: الـ thread بيرجع للـ pool وقت الانتظار
public async Task<IActionResult> Get(int id, CancellationToken ct)
{
    var order = await db.Orders.FindAsync([id], ct);   // thread released while the DB works
    var rate  = await http.GetStringAsync(ratesUrl, ct);
    return Ok(order);
}

الفايدة في : نفس عدد الـ threads يستحمل requests أكتر بكتير. ودي scalability مش speed: الـ request الواحد مش بيبقى أسرع.

#async و await بيشتغلوا إزاي من جوّهHow async/await works under the hood

مثال خطوة بخطوة
  1. طلب محتاج سعر شحن من خدمة خارجية. إرسال الطلب يستغرق وقتًا قصيرًا، لكن الرد قد يصل لاحقًا؛ أغلب المدة انتظار وليست حسابات.
  2. عند await لمهمة غير مكتملة، الدالة تحفظ ما تحتاجه لتكمل ثم تعيد التحكم للمستدعي. عند اكتمال المهمة يستكمل التنفيذ من بعد await.
  3. الفائدة إن thread لا يلزم يظل محجوزًا طول انتظار . await لا يخلق thread جديدًا تلقائيًا، والمهمة المكتملة قد تسمح باستمرار التنفيذ مباشرةً.

الـ compiler بيحوّل الـ method الـ async لـ state machine (class فيه MoveNext() وحالة بتقول وصلنا لأنهي await).

  1. الـ method بتشتغل عادي (synchronously) لحد أول await.
  2. لو الـ task اللي بتستناه خلص فعلاً (زي cache hit)، بتكمّل على طول من غير ما تسيب الـ thread.
  3. لو لسه ماخلصش، بتسجّل continuation ("كمّل من هنا لما يخلص")، وترجّع Task مش مكتمل للي ناداها، والـ thread بيتحرر.
  4. مع network I/O الـ async الحقيقي، مفيش thread مستني: الـ بيبلّغ لما الداتا توصل. بعض async للملفات أو providers ممكن تشغّل الشغل الـ blocking على thread.
  5. لما العملية تخلص، الـ continuation بيتنفذ على thread من الـ ThreadPool (أو على الـ thread في تطبيقات الـ desktop)، ويكمّل من نفس النقطة بكل المتغيرات.
  6. لو حصل exception، بيتخزن جوه الـ Task، ويترمي تاني لما تعمل await.
public async Task<decimal> GetTotalAsync(Guid orderId, CancellationToken ct)
{
    var order = await repo.GetAsync(orderId, ct);         // may suspend here
    var rate  = await fx.GetRateAsync(order.Currency, ct); // and here
    return order.Total * rate;                    // resumes with locals intact
}

#Thread و Task و ThreadPoolThread vs Task vs ThreadPool

ThreadTask
هو إيهthread حقيقي في الـ ، وليه stack خاص (حوالي 1 MB)وعد بنتيجة لعملية هتخلص بعدين
التكلفةغالي في الإنشاء والـ memoryرخيص، والشغل بيستخدم threads من الـ ThreadPool
النتيجةمفيش return value مباشرTask<T> بيرجّع قيمة
Exceptionsلو مااتمسكتش بتوقّع الـ process كلهبتتخزن وتطلع عند الـ await
الإلغاء والتركيبيدويCancellationToken و WhenAll و WhenAny
وقت انتظار network الحقيقيواقف (blocked)ممكن ميبقاش فيه thread خالص

الـ ThreadPool: مجموعة threads جاهزة بيعيد استخدامها الـ runtime. والـ بيخدم كل request على thread منها. ولو عملت block للـ threads دي (.Result)، الـ pool بيخلص، ودي اسمها thread pool starvation.

#Task.Run و WhenAll و WhenAnyTask.Run, WhenAll and WhenAny

Task.Run بيحط شغل على thread من الـ pool. مفيد في الـ والـ console للشغل التقيل على الـ . أما في فإنت أصلاً على thread من الـ pool، فلفّ الـ بيه overhead بس.

❌ Task.Run حوالين I/O في
var order = await Task.Run(() => db.Orders.Find(id));   // still blocks a pool thread
✅ استخدم الـ async API الحقيقي
var order = await db.Orders.FindAsync([id], ct);
// WhenAll: start everything first, then wait for ALL → total time ≈ the slowest call
var stockTask   = inventory.GetStockAsync(sku, ct);
var priceTask   = pricing.GetPriceAsync(sku, ct);
var reviewsTask = reviews.GetAsync(sku, ct);
await Task.WhenAll(stockTask, priceTask, reviewsTask);           // (1)
var page = new ProductPage(                                      // (2) all complete
    await stockTask, await priceTask, await reviewsTask);

// WhenAny: continue when the FIRST task finishes
var winner = await Task.WhenAny(primary.GetAsync(ct), replica.GetAsync(ct));
var data = await winner;                       // (3) observe result or exception

// WaitAsync (.NET 6+) times out the wait; the CTS times out the operation
using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
timeoutCts.CancelAfter(TimeSpan.FromSeconds(5));
var receipt = await gateway.ChargeAsync(order, timeoutCts.Token)   // (4)
    .WaitAsync(TimeSpan.FromSeconds(5), ct);
🧾 شرح الكود
  1. (1) التلات calls بيبدأوا مع بعض. لو كل واحد 100ms، الإجمالي حوالي 100ms مش 300ms. ولو واحد فشل، الـ await بيرمي أول exception، والباقيين في Exception.InnerExceptions.
  2. (2) الـ tasks خلصت فعلاً، فالـ await هنا بيرجّع على طول.
  3. (3) WhenAny بيرجّع الـ task اللي خلص الأول، ومش بيرمي exception، فلازم تعمل await للفايز.
  4. (4) WaitAsync بيوقف الانتظار بعد 5 ثواني بس. إلغاء العملية محتاج timed CancellationTokenSource يتبعت للعملية، زي timeoutCts.Token هنا؛ والـ provider ممكن ما يحترمش الإلغاء.
فخ مشهور: ماتعملش WhenAll على queries على نفس الـ . الـ DbContext مش thread-safe وهيرمي exception. استخدم IDbContextFactory واعمل context لكل task، أو نفّذهم ورا بعض.

#CancellationTokenCancellation

المستخدم ممكن يقفل صفحة التقرير، أو الـ request ينتهي قبل ما قاعدة البيانات تخلص. بدل ما العملية تفضل شغالة بلا داعي، caller يمرر إشارة إلغاء، وكل طبقة تختار كيف تستجيب لها. الكود يوضح تمرير الإشارة وفحصها؛ الإلغاء تعاوني ومش بيقتل thread بالقوة.

[HttpGet("report")]
// (1) ct is cancelled when the client disconnects
public async Task<IActionResult> Report(CancellationToken ct)
{
    // (2) pass it everywhere
    var rows = await db.Sales.Where(s => s.Year == 2026).ToListAsync(ct);
    foreach (var row in rows)
    {
        ct.ThrowIfCancellationRequested();   // (3) check inside long CPU loops
        Process(row);
    }
    return Ok();
}

// combine the request token with your own timeout
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
cts.CancelAfter(TimeSpan.FromSeconds(10));                             // (4)
await partnerApi.SyncAsync(cts.Token);
🧾 شرح الكود
  1. (1) بيديك بيتلغي لو العميل قفل الصفحة، فمفيش داعي تكمّل تقرير تقيل.
  2. (2) مرّره لكل async call. والـ بيمرره للـ database provider، بس مش كل provider بيحترم الإلغاء.
  3. (3) في الـ loops الطويلة، اتشيك بنفسك.
  4. (4) Linked token: بيتلغي لو العميل مشي أو عدّت 10 ثواني.

الإلغاء تعاوني (cooperative): الـ token مش بيوقف الكود بالعافية، الكود هو اللي بيشيك عليه. ولما يتلغي بيترمي OperationCanceledException، وده مش error، فماتسجّلوش كـ error في الـ logs. EF Core بيمرر token للـ database provider، لكن مش مضمون إن كل provider ينفّذ الإلغاء.

في العمليات الحرجة: بعد ما تبعت charge لـ payment gateway، ماتلغيش العملية لأن العميل قفل الصفحة، لأن الدفع ممكن يكون حصل. استخدم CancellationToken.None للخطوات اللي لازم تكمل، وخلّي فيه reconciliation.

#Race conditions و lock و SemaphoreSlim و InterlockedRace conditions and synchronization primitives

مثال خطوة بخطوة
  1. المخزون قطعة واحدة. طلب A قرأ 1، وبعده طلب B قرأ 1 قبل ما A يحفظ. كل واحد قرر إن البيع مسموح؛ القراءة ثم القرار ثم الكتابة كانت سلسلة غير محمية.
  2. داخل عملية واحدة ممكن تحمي الحالة المشتركة بقفل مناسب. لكن لو عندك نسختان من السيرفر، قفل الذاكرة في النسخة الأولى لا يحمي الثانية.
  3. للمخزون المخزن في قاعدة البيانات، مثال الحل تعديل مشروط ذري: أنقص الكمية فقط إذا كانت أكبر من صفر ثم تحقق من عدد الصفوف المتأثرة. حدد مكان مشاركة الحالة قبل اختيار الأداة.

اتنين بيسحبوا من نفس الحساب في نفس اللحظة: الاتنين قروا الرصيد 100، والاتنين سحبوا 80، والاتنين كتبوا 20. فاتسحب 160 من 100! وده بيحصل لما أكتر من thread يقرا ويعدّل نفس الداتا من غير تنظيم.

private int _count;
// NOT atomic: read → add → write, so concurrent updates get lost
public void Hit() => _count++;

// 1) lock: one thread at a time (synchronous code only)
// .NET 9+ Lock type (before: private readonly object _gate = new();)
private readonly Lock _gate = new();
public void Add(Item i) { lock (_gate) { _items.Add(i); _total += i.Price; } }

// 2) Interlocked: atomic operations on a single value
Interlocked.Increment(ref _count);

// 3) SemaphoreSlim: works with await, and can allow N at a time
private readonly SemaphoreSlim _refresh = new(1, 1);
public async Task RefreshTokenAsync(CancellationToken ct)
{
    await _refresh.WaitAsync(ct);
    try { _token = await auth.GetTokenAsync(ct); }
    finally { _refresh.Release(); }                // always release in finally
}
الأداةإمتى
lock / Lockblock قصير sync. وماينفعش await جوه lock، والـ compiler بيمنعك.
Interlockedعداد أو flag واحد، وهو الأسرع.
SemaphoreSlimتحمي كود async، أو تحدد عدد العمليات المتوازية (throttling).
ConcurrentDictionary / ConcurrentQueuecollections آمنة جاهزة.
Channel<T>producer و consumer async في الـ memory.
المهم في الانترفيو: كل الأدوات دي جوه process واحد. لو عندك 4 instances من الـ ، الـ lock مش هيحمي حاجة بينهم. وللداتا المشتركة: الـ database (concurrency ، أو UPDATE ... WHERE Stock >= @qty، أو row lock)، أو distributed lock.

#Deadlocks وأشهر أخطاء الـ asyncDeadlocks and common async mistakes

  1. .Result و .Wait() (sync over async): بيعمل block للـ thread. وفي الـ أو ASP.NET القديم بيعمل : الـ thread مستني الـ task، والـ task مستني يرجع على نفس الـ thread. وفي مفيش SynchronizationContext، فمفيش deadlock، بس فيه thread pool starvation.
  2. async void: مفيش Task ترجع، فمحدش يقدر يعمل await، والـ exception بتوقّع الـ process. استخدمه في الـ event handlers بس.
  3. نسيان await: العملية بتشتغل fire-and-forget، والـ exceptions بتضيع، والـ ممكن يتعمله dispose قبل ما تخلص.
  4. Fire-and-forget جوه request: _ = SendEmailAsync() بيكمّل بعد ما الـ scope يتقفل. استخدم queue أو background job أو .
  5. عمليتين مع بعض على نفس الـ DbContext.
  6. lock حوالين await: مش مسموح، استخدم SemaphoreSlim.
  7. عدم تمرير الـ CancellationToken.

ConfigureAwait(false): بيقول للـ await "مش محتاج ترجع على نفس الـ context". مهم في الـ libraries والـ UI. وفي كود ASP.NET Core نفسه مالوش تأثير، لأن مفيش SynchronizationContext.

Deadlock في الـ locks: thread 1 ماسك A ومستني B، و thread 2 ماسك B ومستني A، فالاتنين واقفين للأبد. والحل ترتيب ثابت للـ locks، و critical sections قصيرة، و timeouts. ونفس الكلام في الـ database .

#Parallel و async و ChannelsParallelism vs asynchrony, and Channels

Async (concurrency)Parallel (parallelism)
الهدفماتضيعش threads وإنت مستنيتستخدم كذا core مع بعض
نوع الشغل-bound-bound
الأدواتasync و await و WhenAllParallel.For و Parallel.ForEachAsync و
// CPU-bound: spread across cores
Parallel.For(0, images.Length, i => images[i] = Resize(images[i]));

// I/O-bound with a concurrency limit (.NET 6+)
var options = new ParallelOptions { MaxDegreeOfParallelism = 8, CancellationToken = ct };
await Parallel.ForEachAsync(skus, options,
    async (sku, token) => await SyncStockAsync(sku, token));

// Channel: in-memory async queue; bounded = back-pressure when consumers are slow
var channel = Channel.CreateBounded<OrderPlaced>(capacity: 1000);
await channel.Writer.WriteAsync(evt, ct);                                        // producer
await foreach (var e in channel.Reader.ReadAllAsync(ct)) await HandleAsync(e);   // consumer

جملة تفتكرها: "Concurrency is about dealing with many things at once; parallelism is about doing many things at once."

أسئلة الانترفيو

راجع فهمك

ليه الاختيار ده مناسب؟ إمتى ممكن ماينفعش؟ وإيه اللي هيتغير لو الحمل زاد أو ظهر فشل؟

4.1What is the difference between synchronous and asynchronous programming?Junior

الـ Sync بيوقف الـ thread لحد ما العملية تخلص. والـ Async بيحرر الـ thread وقت الانتظار ويكمّل لما النتيجة تيجي. وفي الـ الفايدة scalability مش سرعة الـ request الواحد.

الإجابة النموذجية (English)

Synchronous code blocks the thread until an operation completes. Asynchronous code starts the operation, releases the thread while waiting, and continues when the result arrives. In a web that lets the same thread pool handle many more concurrent requests during , so it improves and scalability rather than the speed of a single request.

4.2How do async and await work under the hood?Mid

الـ compiler بيحوّل الـ method لـ state machine. وعند كل await على task لسه ماخلصش، بيسجّل continuation ويرجّع Task غير مكتمل، فالـ thread بيتحرر. ولما العملية تخلص، بيكمّل من نفس النقطة على thread من الـ pool.

الإجابة النموذجية (English)

The compiler rewrites an async method into a state machine. At each await, if the task isn't complete, the method registers a continuation and returns an incomplete Task to its caller, freeing the thread. During no thread waits; the signals completion. The continuation then resumes the state machine at the next step, on a thread-pool thread in or on the captured context in apps. Exceptions are stored in the task and rethrown at the await.

4.3What is the difference between a Task and a Thread?Junior

الـ Thread هو thread غالي. أما الـ Task فهو وعد بنتيجة: رخيص، وبيستخدم الـ ThreadPool، وبيدعم return value و exceptions و cancellation والتركيب.

الإجابة النموذجية (English)

A thread is an operating-system thread with its own stack, expensive to create. A Task is a higher-level representation of an asynchronous operation and its eventual result. work in tasks runs on pooled threads, and tasks don't hold a thread while waiting. Tasks also give you return values, exception propagation, cancellation and composition, so modern code uses tasks rather than raw threads.

4.4What is Task.Run and when should you use it?Mid

بيحط شغل على thread من الـ pool. مفيد في الـ والـ console. أما في ، فماتلفّش بيه علشان "يبقى async"، لأنه بينقل الـ blocking لـ thread تاني بس.

الإجابة النموذجية (English)

Task.Run queues a to the thread pool. I use it to offload -bound work from a thread, or to run independent CPU-heavy pieces in parallel. In I don't wrap synchronous in Task.Run to make it look async; that just moves the blocking to another pool thread. I use real async instead.

4.5What is the difference between Task.WhenAll and Task.WhenAny?Mid

الـ WhenAll بيكمّل لما كلهم يخلصوا، والوقت على قد أبطأ واحد. والـ WhenAny بيكمّل مع أول واحد يخلص، وبيرجّع الـ task نفسه، ومفيد للـ timeouts أو أسرع رد.

الإجابة النموذجية (English)

Task.WhenAll completes when all tasks complete, so independent operations take about as long as the slowest one; awaiting it rethrows the first exception while the task's Exception holds all of them. Task.WhenAny completes when the first task completes and returns that task without throwing, so you await it to get its result. I use WhenAll to run independent calls concurrently, and WaitAsync or WhenAny for timeouts.

4.6What are the most common async/await mistakes?Mid

.Result و .Wait()، و async void، ونسيان await، و fire-and-forget جوه request، وعمليتين على نفس الـ ، و lock حوالين await، وعدم تمرير الـ CancellationToken.

الإجابة النموذجية (English)

Blocking on tasks with .Result or .Wait, which under a synchronization context and starves the thread pool in ; async void outside event handlers; forgetting to await, which silently drops exceptions; fire-and-forget work that outlives the request's scope; concurrent operations on one ; trying to lock across an await; and not passing the CancellationToken through.

4.7Why is async void dangerous?Junior

مفيش Task، فمحدش يقدر يعمل await أو يعرف إمتى خلصت، والـ exception مش بتتمسك وبتوقّع الـ process. مسموح بيه في الـ event handlers بس.

الإجابة النموذجية (English)

An async void method returns nothing awaitable, so callers can't wait for it or observe failures. Its exceptions are raised directly on the synchronization context or thread pool and usually crash the process. It exists only for event handlers; everything else should return Task or ValueTask.

4.8What is the difference between parallel and asynchronous programming?Mid

الـ Async: ماتعملش block وإنت مستني . والـ Parallel: تستخدم كذا core مع بعض لشغل . وتقدر تجمعهم بـ Parallel.ForEachAsync.

الإجابة النموذجية (English)

Asynchronous programming is about not blocking while waiting, mostly on , and can run on very few threads. Parallel programming splits -bound work across cores to finish sooner, with Parallel.For, or multiple tasks. They combine well, for example Parallel.ForEachAsync processes many I/O calls with a concurrency limit.

4.9What is a CancellationToken and how do you use it?Mid

إشارة إلغاء تعاونية. بيديك واحد مع كل request، ومرّره لكل async call. وللـ timeouts، اعمل linked source ومعاه CancelAfter.

الإجابة النموذجية (English)

It's a cooperative cancellation signal created by a CancellationTokenSource. Code receiving it checks it or passes it to that support it, which throw OperationCanceledException when it fires. In , actions receive a that cancels when the client disconnects, and I flow it through and HttpClient calls. For timeouts I create a linked source with CancelAfter.

4.10What does ConfigureAwait(false) do, and do you need it in ASP.NET Core?Senior

بيقول إن الـ continuation مش لازم يرجع للـ SynchronizationContext الأصلي. مهم في الـ libraries والـ . وفي كود نفسه مالوش تأثير.

الإجابة النموذجية (English)

It tells the awaiter not to resume on the captured synchronization context. In apps and classic ASP.NET that avoids and needless context switches, so library code uses it everywhere. has no synchronization context, so in application code it makes no difference, though libraries still use it because they don't know where they'll run.

4.11Task vs ValueTask: when would you use ValueTask?Senior

الـ ValueTask struct، فلو الـ method غالباً بترجع synchronously (زي cache hit) مفيش . وليه قيود: ماتعملهوش await مرتين، ولا تعمل block عليه قبل ما يخلص. فالافتراضي Task، و ValueTask في الـ hot paths بس.

الإجابة النموذجية (English)

ValueTask is a struct that avoids allocating a Task when a method usually completes synchronously, such as returning a cached value. It has rules: await it only once and don't block on it before it completes. I default to Task and use ValueTask on hot paths where profiling shows the matter.

4.12What is a race condition and how do you prevent it?Mid

أكتر من thread بيعدّلوا نفس الداتا من غير تنظيم، فالنتيجة بتعتمد على التوقيت. والحل ، أو lock، أو Interlocked، أو SemaphoreSlim، أو concurrent collections. وبين الـ instances: الـ database.

الإجابة النموذجية (English)

A is when the outcome depends on the timing of threads touching shared mutable state, such as two threads incrementing a counter and losing an update. I avoid shared mutable state where possible, use data, or synchronize with lock, Interlocked, SemaphoreSlim or concurrent collections. Across multiple service instances, I enforce correctness in the database with , atomic conditional updates or row locks.

4.13How does the lock keyword work?Mid

بيتحوّل لـ Monitor.Enter و Monitor.Exit جوه try/finally، أو لـ Lock.EnterScope() لو بتستخدم نوع Lock في 9 وما بعده. واقفل على object private readonly، مش this ولا string، ومفيش await جوه.

الإجابة النموذجية (English)

lock ensures only one thread at a time runs the block and always releases it, compiling to Monitor.Enter and Exit in try/finally, or to Lock.EnterScope when you lock on the System.Threading.Lock type introduced in 9. I lock on a private readonly object, never on this, a type or a string, keep the section short, and never await inside it; for async code I use SemaphoreSlim.

4.14What is a deadlock and how do you avoid it?Mid

اتنين (أو أكتر) كل واحد ماسك مورد ومستني التاني للأبد. والحل ترتيب ثابت للـ locks، و critical sections قصيرة، و timeouts، وماتعملش sync over async. وفي الـ : قصيرة، و retry للـ victim.

الإجابة النموذجية (English)

A happens when two or more threads or each hold a resource and wait for one held by another, so none can proceed. I avoid it by acquiring locks in a consistent order, keeping critical sections and transactions short, using timeouts, and never blocking on async code. Databases detect deadlocks and kill one transaction as the victim, so those operations should be retried.

4.15What is thread pool starvation and how do you detect it?Senior

كل threads الـ pool واقفة (blocked)، غالباً بسبب .Result، والـ pool بيزوّد threads ببطء. فالـ بتطلع جداً مع إن الـ فاضي. والاكتشاف بـ dotnet-counters (ThreadPool Queue Length بيطلع). والحل async بالكامل.

الإجابة النموذجية (English)

It happens when pool threads are blocked, typically by sync-over-async code, so queued work waits while the pool slowly injects new threads. explodes while stays low. dotnet-counters shows a growing thread-pool queue length and thread count. The fix is making the call chain async all the way down, not raising the minimum thread count.

4.16What are concurrent collections, and what is a Channel?Mid

collections آمنة لأكتر من thread: ConcurrentDictionary و ConcurrentQueue و ConcurrentBag. وخد بالك إن GetOrAdd ممكن ينده الـ factory أكتر من مرة. أما الـ Channel<T> فـ queue async للـ producer والـ consumer، ولو bounded بيعمل back-pressure.

الإجابة النموذجية (English)

Concurrent collections like ConcurrentDictionary and ConcurrentQueue are thread-safe without manual locking. A subtle point: GetOrAdd may run its factory more than once under contention, so for one-time creation I store Lazy values. Channel of T is an async producer-consumer queue; a bounded channel applies back-pressure when consumers fall behind, which makes it ideal for in-process pipelines and background workers.

4.17What is the difference between IEnumerable and IAsyncEnumerable?Mid

الـ IAsyncEnumerable<T> بيطلّع العناصر واحد واحد ومعاها await (await foreach). مفيد للـ streaming من الـ أو الـ من غير ما تحمّل كله في الـ memory.

الإجابة النموذجية (English)

IAsyncEnumerable produces and consumes a sequence asynchronously, awaiting between items with await foreach and async iterators. It's useful for streaming large query results from or returning a streamed array from an endpoint without loading everything into memory or blocking threads.

اختبر نفسك

إجابات الاختبار وعلامات إكمال المحاور بتتحفظ على المتصفح ده، ومش بتتنقل لأجهزة تانية.

النتيجة: 0 / 0

What is the main benefit of async/await in an ASP.NET Core API?

الفايدة scalability: نفس الـ threads تخدم requests أكتر.

Calling .Result on a Task inside ASP.NET Core mainly causes...

مفيش SynchronizationContext، فمفيش كلاسيكي، بس الـ threads بتتحجز.

Three independent 100 ms calls awaited with Task.WhenAll take about...

بيشتغلوا مع بعض، فالوقت على قد أبطأ واحد.

You need to await inside a critical section. Which primitive?

مفيش await جوه lock. والـ SemaphoreSlim بيدعم WaitAsync.

A C# lock protects a stock counter, but the API runs on 4 instances. Is stock safe?

الحماية الحقيقية في الـ ، زي UPDATE ... WHERE Stock >= @qty.

Two EF Core queries on the same DbContext are started with Task.WhenAll. What happens?

الـ مش thread-safe. استخدم IDbContextFactory.

Where is async void acceptable?

غير كده رجّع Task.

Best tool for CPU-bound work across cores?

الـ async للـ ، والـ parallel للـ .