TPL Flashcards

Task parallel library related questions (68 cards)

1
Q

What does ConfigureAwait(false) do?

A

Continues the async operation without capturing the current synchronization context, allowing the continuation to run on any thread. Improves performance in libraries where returning to the original context isn’t required.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Why should you usually use ConfigureAwait(false) in libraries?

A

To avoid deadlocks in UI or ASP.NET contexts and improve performance by not forcing the continuation back to the original context.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Difference between Task.Run(async () => …) and await SomeAsyncMethod()?

A
  • Task.Run: Queues work to ThreadPool; may create a new thread.
  • await SomeAsyncMethod(): Asynchronously waits for the operation without blocking the calling thread; continuation may run on the original context unless ConfigureAwait(false) is used.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

What happens if you forget await in an async method?

A

The async method starts but the calling method doesn’t wait for it, potentially causing unobserved exceptions or race conditions

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Difference between blocking and non-blocking calls in HTTPClient: Post() vs PostAsync()?

A

Post(): Synchronously blocks the calling thread until the request completes.

PostAsync(): Asynchronously sends the request; doesn’t block the calling thread.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

When should you use Task.Run in an ASP.NET application?

A

Only for CPU-bound work that would otherwise block the request thread. For I/O-bound async operations, just use await without Task.Run.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

What is the risk of using Task.Run(() => GetDataAsync()) without await?

A

You may create a nested Task, lose exceptions, and potentially run into unobserved exceptions or unfinished work.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

What is the risk of not using ConfigureAwait(false) in library code?

A

In a UI or ASP.NET context, awaiting may try to resume on the original synchronization context, potentially causing deadlocks or performance issues.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

What happens when multiple exceptions occur in parallel tasks?

A

They are wrapped into an AggregateException.

You were right — Task.WaitAll, Task.WhenAll, or similar APIs wrap multiple exceptions in an AggregateException.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

What is the difference between Wait() and await when handling exceptions?

A

Wait() (or Result) will throw an AggregateException, even if there’s just a single exception.

await will unwrap the exception and throw the original exception (not wrapped).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Q3: What happens if the first completed task in Task.WhenAny throws an exception?

A

The exception is stored in the returned task.

Task.WhenAny itself doesn’t throw — it just returns the first completed task (success, faulted, or canceled).

You must await the returned task to observe the exception.

var t1 = Task.Run(() => throw new InvalidOperationException());
var t2 = Task.Delay(1000);

var first = await Task.WhenAny(t1, t2);

try
{
await first; // Exception thrown here
}
catch (InvalidOperationException ex)
{
Console.WriteLine($”Caught: {ex.Message}”);
}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

IsFaulted and Task.Exception property

A

var t = Task.Run(() => throw new Exception(“Error”));

t.Wait(); // or just let it finish

if (t.IsFaulted)
Console.WriteLine($”Task failed: {t.Exception.InnerException.Message}”);

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

What is Task.GetAwaiter()

A

Every Task (or Task<T>) has a GetAwaiter() method.</T>

Normally, you don’t call it yourself — it’s used by the C# compiler when you write await task;.

Example:
Task<int> task = Task.Run(() => 42);
var awaiter = task.GetAwaiter(); // returns a TaskAwaiter<int></int></int>

int value = await task;
into something like:

var awaiter = task.GetAwaiter();
if (!awaiter.IsCompleted)
{
awaiter.OnCompleted(() => { /* resume method */ });
}
int value = awaiter.GetResult();
👉 You don’t normally call GetAwaiter() directly — unless you’re writing low-level libraries (e.g., custom awaitable types).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Whast is Task.GetResult()

A

Used internally by await.

Returns the result if task completed successfully, otherwise:

Throws the original exception (not AggregateException).

⚠️ If task is not completed, GetResult() will block until completion.
Task<int> task = Task.Run(() => 1 / 0);
var awaiter = task.GetAwaiter();
try
{
int result = awaiter.GetResult();
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType()); // DivideByZeroException (original)
}</int>

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Task.Wait()

A

Blocks the current thread until the task is finished.

If the task faulted, it throws AggregateException.

Should be avoided in async code (can cause deadlocks in UI or ASP.NET contexts).

Use await instead of Wait() whenever possible.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

✅ Prefer

await (best choice for async code)

Lets compiler handle GetAwaiter/GetResult

Exceptions are automatically unwrapped

A

🚫 Avoid (unless you have good reason)

Wait() and .Result → cause blocking & AggregateException

GetAwaiter() + GetResult() manually → only for low-level libraries or writing custom awaitable logic

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

: What does Task.Wait() do, and why can it be dangerous?

A

Wait() blocks the calling thread until the task finishes. If the task throws an exception, it wraps it in an AggregateException. It can cause deadlocks (especially in UI contexts like WinForms/WPF/ASP.NET) and should be avoided in async code.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

What does accessing Task.Result do, and why should it be used carefully?

A

Result blocks the calling thread until the task finishes and then returns the result. If an exception occurs, it’s wrapped in an AggregateException. Like Wait(), it can deadlock in async contexts. Use await instead in async code.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

What does GetAwaiter() return, and when is it useful?

A

GetAwaiter() returns a TaskAwaiter (or ConfiguredTaskAwaiter), which allows fine-grained control over awaiting a task. Normally you don’t call it directly, since await uses it under the hood, but it’s useful when building custom awaiters or writing lower-level async infrastructure.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

: What does GetResult() do when called on a task’s awaiter?

A

GetResult() blocks until the task is finished and returns the result. If the task failed, it throws the original exception (not wrapped in AggregateException). This is different from Wait() and Result.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

Why is await task usually preferred over task.Wait() or task.Result?

A

Because await is non-blocking, avoids deadlocks, unwraps exceptions automatically, and allows the continuation to resume on the right synchronization context. It’s the recommended way in modern async code.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

✅ So what’s the difference?

Wait() & Result: block + wrap exception in AggregateException.

GetAwaiter().GetResult(): block + throw original exception.

That’s the real distinction.

None of them are truly “non-blocking” when the task isn’t finished. They all block the calling thread if the task is still running. The only non-blocking way is using await task.

A

⚠️ Why avoid them?

They block threads → kills the benefit of async.

In UI apps (WinForms, WPF, ASP.NET old synchronization context), they can cause deadlocks.

Best practice: always use await.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

What is Task.ContinueWith used for?

A

It creates a continuation task that runs after the antecedent task completes, regardless of its final state (success, fault, canceled).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

What does ContinueWith return?

A

It returns a new Task (or Task<TResult>) representing the continuation, not the original task.</TResult>

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
How do you ensure a continuation runs only when the antecedent completes successfully?
Use ContinueWith with TaskContinuationOptions.OnlyOnRanToCompletion.
26
How do you ensure a continuation runs only when the antecedent fails?
Use ContinueWith with TaskContinuationOptions.OnlyOnFaulted.
27
How do you ensure a continuation runs only when the antecedent is canceled?
Use ContinueWith with TaskContinuationOptions.OnlyOnCanceled.
28
What’s the difference between ContinueWith and await?
await automatically marshals exceptions, cancellation, and resumes on the captured context, while ContinueWith is lower-level and requires explicit handling of state and exceptions.
29
When should you prefer ContinueWith over await?
Prefer ContinueWith when you need fine-grained control over continuation conditions, custom TaskScheduler, or chaining complex task graphs. Otherwise, use await for readability and simplicity.
30
Can ContinueWith run synchronously?
Yes, by specifying TaskContinuationOptions.ExecuteSynchronously, but only if the antecedent is already completed.
31
Can multiple continuations be attached to the same task?
Yes, you can attach multiple ContinueWith calls to one task, and each continuation will run independently when the task completes.
32
What happens if an exception occurs in a continuation?
The exception is captured in the continuation task. If unobserved, it may cause the process to terminate unless handled.
33
How do you wait for a Task to complete and propagate its exception?
Use task.Wait() or task.Result; both block the current thread and throw exceptions wrapped in AggregateException.
34
How do you retrieve the result of a completed Task without blocking?
Use await task; it will asynchronously wait without blocking the current thread.
35
How do you run a continuation only if the Task completes successfully?
Use ContinueWith with TaskContinuationOptions.OnlyOnRanToCompletion.
36
How do you run a continuation if the Task fails?
Use ContinueWith with TaskContinuationOptions.OnlyOnFaulted.
37
How do you run a continuation if the Task is canceled?
Use ContinueWith with TaskContinuationOptions.OnlyOnCanceled.
38
How do you synchronously retrieve the result of a Task without wrapping in AggregateException?
Use task.GetAwaiter().GetResult(); it throws the original exception instead of AggregateException but blocks if the Task isn’t completed.
39
How do you combine await with ContinueWith to handle both success and failure?
Use await for normal flow, and attach ContinueWith for faulted/canceled cases. Example: `task.ContinueWith(t => HandleError(t), TaskContinuationOptions.NotOnRanToCompletion); var result = await task;`
40
What Thread.Sleep() does?
Thread.Sleep(2000); // blocks current thread for 2 seconds Behavior: Blocks the current thread completely. Thread cannot do anything else during the sleep. In a UI app (WPF/WinForms), it freezes the UI if used on the UI thread. In a web app (ASP.NET Core), blocking a thread reduces scalability because the thread cannot handle other requests. Use cases: Rarely recommended in async scenarios. Sometimes used in console apps or low-level threading scenarios.
41
What Task.Delay() does?
await Task.Delay(2000); // asynchronously waits for 2 seconds Behavior: Non-blocking delay. The thread is released back to the thread pool while waiting. In async methods, other work can continue during the delay. Perfect for UI responsiveness and highly scalable web apps. Use cases: Simulating asynchronous work. Waiting between retries in async code. Throttling operations without blocking threads.
42
Difference between Thread.Sleep() and Task.Delay()
Feature Thread.Sleep Task.Delay Thread blocking ✅ Blocks thread ❌ Does NOT block thread Async-friendly ❌ No ✅ Yes (with await) UI responsivenes ❌ Freezes UI ✅ UI stays responsive Scalability ❌ Bad for servers✅ Good for servers ⚡ Rule of Thumb: Use Thread.Sleep only in synchronous, blocking code. Use Task.Delay in async code to avoid blocking threads.
43
44
00What is TPL in .NET?
The Task Parallel Library (TPL) provides high-level APIs for creating and managing tasks for parallel and asynchronous programming without manually handling threads.
45
Why limit concurrency in TPL?
To prevent CPU, memory, or I/O resource exhaustion, avoid thread pool starvation, and optimize throughput.
46
What is SemaphoreSlim used for in TPL?
To control the number of concurrently running tasks, especially for async operations.
47
How do you use SemaphoreSlim with async tasks?
await semaphore.WaitAsync(); try { await DoWorkAsync(); } finally { semaphore.Release(); }
48
What is MaxDegreeOfParallelism?
A property in ParallelOptions that sets the maximum number of concurrent tasks in Parallel.For, Parallel.ForEach, or Parallel.Invoke.
49
How do you limit concurrency in Parallel.ForEach?
var options = new ParallelOptions { MaxDegreeOfParallelism = 4 }; Parallel.ForEach(items, options, item => ProcessItem(item));
50
Difference between SemaphoreSlim and MaxDegreeOfParallelism?
SemaphoreSlim works for async tasks and gives fine-grained control; MaxDegreeOfParallelism is simpler and mainly for CPU-bound synchronous operations.
51
Can Task.WhenAll control concurrency?
No, Task.WhenAll waits for all tasks to complete but does not limit how many run concurrently.
52
How can TPL Dataflow help limit concurrency?
ActionBlock or TransformBlock allow MaxDegreeOfParallelism to limit parallel processing of messages.
53
Example of limiting concurrency with ActionBlock:
var options = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 5 }; var block = new ActionBlock(async item => await DoWorkAsync(item), options); items.ForEach(block.Post); block.Complete(); await block.Completion;
54
Why is it dangerous to start too many tasks at once?
It can exhaust the thread pool, increase memory usage, cause high CPU load, and reduce overall performance.
55
How does Parallel.ForEachAsync help with async concurrency?
In .NET 6+, it allows processing async delegates with a MaxDegreeOfParallelism option.
56
Example of Parallel.ForEachAsync limiting concurrency:
await Parallel.ForEachAsync(items, new ParallelOptions { MaxDegreeOfParallelism = 3 }, async (item, token) => { await DoWorkAsync(item); });
57
What is the default degree of parallelism in Parallel.ForEach?
By default, it uses Environment.ProcessorCount to decide the number of concurrent tasks.
58
How can you throttle tasks without SemaphoreSlim?
Using TPL Dataflow, ParallelOptions.MaxDegreeOfParallelism, or a custom task queue with limited workers.
59
Can you mix async and Parallel.ForEach?
You should avoid Parallel.ForEach for async delegates; instead, use Parallel.ForEachAsync or SemaphoreSlim.
60
How do you handle exceptions when limiting concurrency?
Use try/finally to release semaphores and Task.WhenAll to aggregate exceptions.
61
Why is SemaphoreSlim.WaitAsync() preferred over Wait() for async tasks?
WaitAsync() is non-blocking and prevents thread starvation, while Wait() blocks a thread unnecessarily.
62
Can limiting concurrency improve performance for I/O-bound tasks?
Yes, because it prevents overwhelming I/O resources and allows optimal throughput.
63
Can limiting concurrency hurt performance for CPU-bound tasks?
If the limit is too low, it can underutilize CPU cores; the optimal limit is usually equal to the number of logical cores.
64
How can you create a reusable throttling helper for async tasks?
async Task RunThrottledAsync(IEnumerable items, int maxConcurrency, Func action) { var semaphore = new SemaphoreSlim(maxConcurrency); var tasks = items.Select(async item => { await semaphore.WaitAsync(); try { await action(item); } finally { semaphore.Release(); } }); await Task.WhenAll(tasks); }
65
What is the difference between throttling and batching?
Throttling limits concurrency at runtime; batching processes items in fixed-size groups.
66
How do BlockingCollection and producer-consumer patterns relate to concurrency?
They provide a thread-safe queue with controlled consumer concurrency, limiting simultaneous processing.
67
How can you test if your concurrency limit is effective?
By logging task start/end times and ensuring no more than the desired number of tasks run simultaneously.
68
Key best practices for limiting concurrency in TPL:
Use SemaphoreSlim for async tasks. Use MaxDegreeOfParallelism for synchronous CPU-bound tasks. Handle exceptions and release semaphores in finally. Match concurrency limits to resource capacity (CPU cores, I/O bandwidth). Avoid creating too many tasks at once; batch or queue them if necessary.