HttpClient SendAsync chặn luồng chính
Tôi đã viết một ứng dụng winforms nhỏ gửi yêu cầu http đến mọi địa chỉ ip trong mạng cục bộ của tôi để khám phá một thiết bị nhất định của tôi. Trên mặt nạ mạng con cụ thể của tôi có 512 địa chỉ. Tôi đã viết bài này bằng backGroundWorker nhưng tôi muốn dùng thử httpClient và mẫu Async / Await để đạt được điều tương tự. Đoạn mã dưới đây sử dụng một phiên bản httpClient và tôi đợi cho đến khi tất cả các yêu cầu hoàn tất. Vấn đề này là luồng chính bị chặn. Tôi biết điều này bởi vì tôi có một hộp ảnh + tải gif và nó không hoạt ảnh đồng nhất. Tôi đã đặt phương thức GetAsync trong Task.Run như được đề xuất ở đây nhưng điều đó cũng không hoạt động.
private async void button1_Click(object sender, EventArgs e)
{
var addresses = networkUtils.generateIPRange..
await MakeMultipleHttpRequests(addresses);
}
public async Task MakeMultipleHttpRequests(IPAddress[] addresses)
{
List<Task<HttpResponseMessage>> httpTasks = new List<Task<HttpResponseMessage>>();
foreach (var address in addresses)
{
Task<HttpResponseMessage> response = MakeHttpGetRequest(address.ToString());
httpTasks.Add(response);
}
try
{
if (httpTasks.ToArray().Length != 0)
{
await Task.WhenAll(httpTasks.ToArray());
}
}
catch (Exception ex)
{
Console.WriteLine("\thttp tasks did not complete Exception : {0}", ex.Message);
}
}
private async Task<HttpResponseMessage> MakeHttpGetRequest(string address)
{
var url = string.Format("http://{0}/getStatus", address);
var cts = new System.Threading.CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(10));
HttpResponseMessage response = null;
var request = new HttpRequestMessage(HttpMethod.Get, url);
response = await httpClient.SendAsync(request, cts.Token);
return response;
}
Tôi đã đọc một vấn đề tương tự ở đây nhưng chủ đề gui của tôi không hoạt động nhiều. Tôi đã đọc ở đây rằng tôi có thể chạy ra khỏi chủ đề. Đây có phải là vấn đề, làm thế nào tôi có thể giải quyết nó? Tôi biết nó là Send Async vì nếu tôi thay thế mã bằng tác vụ đơn giản bên dưới thì sẽ không bị chặn.
await Task.Run(() =>
{
Thread.Sleep(1000);
});
Trả lời
Vì vậy, một trong những vấn đề ở đây là bạn đang tạo ra hơn 500 nhiệm vụ lần lượt liên tiếp nhanh chóng với thời gian chờ được đặt bên ngoài quá trình tạo tác vụ.
Chỉ vì bạn yêu cầu chạy hơn 500 tác vụ, không có nghĩa là hơn 500 tác vụ sẽ chạy cùng một lúc. Chúng được xếp hàng và chạy khi người lập lịch cho là có thể.
Bạn đặt thời gian chờ tại thời điểm tạo là 10 giây. Nhưng họ có thể ngồi trong bộ lập lịch trong 10 giây trước khi thực hiện.
Bạn muốn các yêu cầu Http của mình hết thời gian chờ một cách tự nhiên, bạn có thể làm như vậy khi tạo HttpClient:
private static readonly HttpClient _httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(10)
};
Vì vậy, bằng cách chuyển thời gian chờ đến HttpClient, phương thức của bạn bây giờ sẽ giống như sau:
private static Task<HttpResponseMessage> MakeHttpGetRequest(string address)
{
return _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, new UriBuilder
{
Host = address,
Path = "getStatus"
}.Uri));
}
Hãy thử sử dụng phương pháp đó và xem liệu nó có cải thiện vấn đề khóa của bạn ở chế độ Gỡ lỗi hay không.
Theo như vấn đề bạn đang gặp phải: Nó bị khóa vì bạn đang ở chế độ Gỡ lỗi và trình gỡ lỗi đang cố gắng nói "này, bạn có một ngoại lệ" 500 lần cùng một lúc vì tất cả chúng đều được tạo cùng một lúc . Chạy nó ở chế độ Phát hành và xem nó có còn khóa không.
Những gì tôi sẽ xem xét làm là phân chia các hoạt động của bạn. Làm 20 cái, sau đó đợi cho đến khi 20 cái đó hoàn thành, làm thêm 20 cái nữa, v.v.
Nếu bạn muốn xem một cách khéo léo để phân chia các nhiệm vụ, hãy cho tôi biết và tôi rất vui được chỉ cho bạn.
Trên .NET Framework, số lượng kết nối đến máy chủ được kiểm soát bởi Lớp ServicePointManager .
Đối với máy khách, giới hạn kết nối mặc định là 2 trên các quy trình máy khách.
Bất kể bạn thực hiện bao nhiêu lệnh gọi HttpClient.SendAsync, chỉ có 2 lệnh hoạt động cùng một lúc.
Nhưng bạn có thể tự quản lý các kết nối .
Trên .NET Core ở đây không phải là khái niệm về trình quản lý điểm dịch vụ và giới hạn mặc định tương đương là int.MaxValue.