SmtpClient.SendAsync标记为异步却返回void,是否为真异步?
Great question! Let’s break this down to understand exactly what’s happening here.
First, the short answer: yes, this method is truly asynchronous—it’s not a synchronous operation masquerading as async. The confusion stems from it using an older .NET asynchronous pattern instead of the modern Task-based approach you might expect.
Why async with a void return?
This method follows the Event-based Asynchronous Pattern (EAP), which was widely used in .NET before the Task-based Asynchronous Pattern (TAP) and async/await became the standard. EAP methods are designed to return void and use events to notify callers when the async operation finishes—here, that’s the SendCompleted event on SmtpClient.
The async keyword is present because the internal implementation uses await to handle asynchronous I/O flow cleanly, but since it’s adhering to the EAP contract, it returns void instead of a Task. It’s worth noting that async void methods are generally discouraged outside of event handlers (since they have tricky exception handling behavior), but this is a legacy design choice to maintain compatibility with older codebases.
How does it handle async without Task?
Under the hood, the method relies on traditional .NET asynchronous mechanisms:
- It uses the Asynchronous Programming Model (APM) pattern, which uses paired methods like
BeginSend/EndSendfor network socket operations (since SMTP is a network protocol). These methods kick off async I/O without blocking the calling thread. - It may also queue work to the
ThreadPoolto offload non-I/O-bound async work, ensuring the main thread stays responsive. - When the operation completes (successfully, with an error, or if cancelled), it triggers the
SendCompletedevent, passing details viaAsyncCompletedEventArgs—including any errors, cancellation status, or a user-provided token you passed in.
How to use this method correctly
To work with this async method properly, you need to subscribe to the SendCompleted event before invoking it:
var smtpClient = new SmtpClient("your-smtp-server-address"); var mailMessage = new MailMessage( "sender@example.com", "recipient@example.com", "Hello!", "This is an async test email." ); // Subscribe to the completion event first smtpClient.SendCompleted += (sender, args) => { if (args.Error != null) { Console.WriteLine($"Email failed to send: {args.Error.Message}"); } else if (args.Cancelled) { Console.WriteLine("Send operation was cancelled."); } else { Console.WriteLine("Email sent successfully!"); } // Clean up resources once done smtpClient.Dispose(); mailMessage.Dispose(); }; // Start the asynchronous send operation smtpClient.SendAsync(mailMessage, null);
A quick modern note
If you’re working with .NET Core 3.0 or later, keep in mind that SmtpClient is marked obsolete. The recommended alternative is using libraries like MailKit, which fully support modern TAP with Task-returning async methods, making async code easier to write and maintain.
内容的提问来源于stack exchange,提问作者will

