Skip to main content

Command Palette

Search for a command to run...

Task.Run vs. QueueUserWorkItem

Updated
2 min read
Task.Run vs. QueueUserWorkItem
F

Performance freak

Using ThreadPool.QueueUserWorkItem:

  • Helps bypass limitations when trying to run async code inside IHttpModule.

  • Does not interfere with the ASP.NET pipeline.

  • Email sending does not block the main thread.

  • Calling SendMailAsync().Wait() is safe here because the operation runs on a separate thread.

  • The .NET ThreadPool manages a set of worker threads in the background, always on standby.

  • QueueUserWorkItem submits a task to this pool—if a thread is available, it runs immediately; if not, it’s queued.

  • That means it does not create a new thread per call, making it lightweight and efficient.

Scope & Behavior:

FeatureQueueUserWorkItemTask.Run
NatureFire-and-forgetSuitable for async/await chains
ASP.NET Web Forms100% safe and compatibleCompatible, but caution required

Sample usage of ThreadPool.QueueUserWorkItem:

ThreadPool.QueueUserWorkItem(_ =>
{
    try
    {
        PublicFunctions.SendMailAsync(body, to, subject).Wait();
    }
    catch (Exception ex)
    {
        // fallback log
    }
});

About Task.Run:

  • A task started via Task.Run(...) may not complete before the HTTP response is sent.

  • If the task takes too long or exceptions pile up, IIS’s thread pool can get overwhelmed.

Task.Run(async () =>
{
    try
    {
        await PublicFunctions.SendMailAsync(...);
    }
    catch (Exception ex)
    {
        // fallback log
    }
});

Which one should you use?

ScenarioRecommendation
Very short and simple tasksQueueUserWorkItem
Targeting .NET 4.0 or earlierQueueUserWorkItem
Involving async/await chainsTask.Run

If you're just sending an error email:

  • The operation is short-lived,

  • No I/O-heavy logic or file/database writing involved,

  • It's a fire-and-forget scenario,

Then the simplest, most reliable and performance-friendly choice is:

ThreadPool.QueueUserWorkItem + .Wait()

Why is QueueUserWorkItem a smart choice?

  • Lightweight: No task scheduling overhead, it directly enqueues to the ThreadPool.

  • Legacy-friendly: 100% compatible with Web Forms and older ASP.NET infrastructure.

  • Ideal for fire-and-forget: You don’t need the result; you just want to trigger the action.

When would Task.Run make more sense?

  • If the task involves multiple awaits (e.g., send email + log + call API),

  • If you need to act based on the result of the task,

In summary:

If you're simply sending an error notification email, there's no need to use Task.Run.

The most sensible, lightweight, and battle-tested solution:
ThreadPool.QueueUserWorkItem with .Wait()

  • Lower resource usage

  • More predictable behavior

  • Works perfectly in global error handlers or other rare but critical paths