求C#实现同时向同一页面发送多请求(如登录)的示例代码
Got it, I’ve been in your shoes before—trying to get multiple requests to fire at the same time instead of waiting for each one to finish sequentially can be tricky if you’re not using the right async patterns. Let’s break this down with a concrete example tailored to your login scenario.
Core Concept: Use Task.WhenAll with Async/Await
The most common mistake here is awaiting each individual request one by one (which runs them serially). Instead, you want to create all your async tasks first, then wait for all of them to complete at once using Task.WhenAll. This is the cleanest, most efficient way to handle concurrent requests in modern C#.
Complete Example Code
First, note that we’ll reuse a single HttpClient instance (critical for performance—creating a new one per request can exhaust connection pools):
using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Json; using System.Threading.Tasks; public class LoginRequestHandler { // Reuse HttpClient across requests (best practice to avoid socket exhaustion) private static readonly HttpClient _httpClient = new HttpClient { BaseAddress = new Uri("https://your-login-api-domain.com/") }; // Model to match your login API's request body structure public class LoginRequest { public string Username { get; set; } public string Password { get; set; } } // Model to match your login API's response structure public class LoginResponse { public bool IsSuccess { get; set; } public string StatusMessage { get; set; } } // Async method to send one login request and return its status private async Task<(bool Success, string Message)> SendSingleLoginRequest(LoginRequest request) { try { var response = await _httpClient.PostAsJsonAsync("api/auth/login", request); response.EnsureSuccessStatusCode(); // Throws if HTTP status is 4xx/5xx var loginResult = await response.Content.ReadFromJsonAsync<LoginResponse>(); return (loginResult.IsSuccess, loginResult.StatusMessage); } catch (HttpRequestException ex) { // Catch request-specific errors without breaking other requests return (false, $"Request failed: {ex.Message}"); } } // Method to send 10 concurrent login requests and process results public async Task RunConcurrentLoginTests() { // Prepare 10 test login requests (customize these as needed) var loginRequests = new List<LoginRequest>(); for (int i = 0; i < 10; i++) { loginRequests.Add(new LoginRequest { Username = $"test_user_{i}", Password = "secure_test_pass_123" }); } // Create a list of tasks (this kicks off all requests in parallel) var loginTasks = new List<Task<(bool Success, string Message)>>(); foreach (var request in loginRequests) { loginTasks.Add(SendSingleLoginRequest(request)); } // Wait for all tasks to complete (this is where concurrency happens) var results = await Task.WhenAll(loginTasks); // Loop through results to check each request's status for (int i = 0; i < results.Length; i++) { var result = results[i]; Console.WriteLine($"Request {i+1}: {(result.Success ? "SUCCESS" : "FAILURE")} - {result.Message}"); } } } // To execute the code: // var handler = new LoginRequestHandler(); // await handler.RunConcurrentLoginTests();
Key Notes to Avoid Common Pitfalls
- Reuse
HttpClient: Never create a newHttpClientfor each request—this leads to socket exhaustion. The static instance above works, or you can use dependency injection in ASP.NET apps. - Don’t await tasks in a loop: If you did
foreach (var request in loginRequests) { await SendSingleLoginRequest(request); }, that would run requests one after another, not concurrently. - Isolate request errors: By catching exceptions inside
SendSingleLoginRequest, a single failed request won’t take down all the others. You’ll get a failure status for that specific request while others continue. - Results stay in order: The
resultsarray matches the order of theloginTaskslist, so you can easily map each result back to its original request.
Why Raw Threads Didn’t Work for You
If you tried using raw threads, you were probably missing the async/await integration, or manual thread management gets messy fast. Task.WhenAll uses the thread pool efficiently without you having to handle thread creation and cleanup—it’s the modern, recommended approach for concurrent I/O operations in C#.
内容的提问来源于stack exchange,提问作者Programmer

