# Task.Run vs. QueueUserWorkItem

### Using `ThreadPool.QueueUserWorkItem`:

* Helps bypass limitations when trying to run async code inside `IHttpModule`.
    
* Does not interfere with the [ASP.NET](http://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:

| Feature | QueueUserWorkItem | [Task.Run](http://Task.Run) |
| --- | --- | --- |
| Nature | Fire-and-forget | Suitable for async/await chains |
| [ASP.NET](http://ASP.NET) Web Forms | 100% safe and compatible | Compatible, but caution required |

Sample usage of `ThreadPool.QueueUserWorkItem`:

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

### About [Task.Run](http://Task.Run):

* A task started via [`Task.Run`](http://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.
    

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

### Which one should you use?

| Scenario | Recommendation |
| --- | --- |
| Very short and simple tasks | ✅ `QueueUserWorkItem` |
| Targeting .NET 4.0 or earlier | ✅ `QueueUserWorkItem` |
| Involving async/await chains | ✅ [`Task.Run`](http://Task.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](http://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`](http://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`](http://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
