HttpClient - Một nhiệm vụ đã bị hủy?


191

Nó hoạt động tốt khi có một hoặc hai nhiệm vụ tuy nhiên gây ra lỗi "Một nhiệm vụ đã bị hủy" khi chúng tôi có nhiều hơn một nhiệm vụ được liệt kê.

nhập mô tả hình ảnh ở đây

List<Task> allTasks = new List<Task>();
allTasks.Add(....);
allTasks.Add(....);
Task.WaitAll(allTasks.ToArray(), configuration.CancellationToken);


private static Task<T> HttpClientSendAsync<T>(string url, object data, HttpMethod method, string contentType, CancellationToken token)
{
    HttpRequestMessage httpRequestMessage = new HttpRequestMessage(method, url);
    HttpClient httpClient = new HttpClient();
    httpClient.Timeout = new TimeSpan(Constants.TimeOut);

    if (data != null)
    {
        byte[] byteArray = Encoding.ASCII.GetBytes(Helper.ToJSON(data));
        MemoryStream memoryStream = new MemoryStream(byteArray);
        httpRequestMessage.Content = new StringContent(new StreamReader(memoryStream).ReadToEnd(), Encoding.UTF8, contentType);
    }

    return httpClient.SendAsync(httpRequestMessage).ContinueWith(task =>
    {
        var response = task.Result;
        return response.Content.ReadAsStringAsync().ContinueWith(stringTask =>
        {
            var json = stringTask.Result;
            return Helper.FromJSON<T>(json);
        });
    }).Unwrap();
}

Ngoại lệ nói gì?
RagtimeWilly

1
Tại sao bạn lấy một CancellationTokentham số và không sử dụng nó?
Jim Aho

1
Lý do cho tôi là xử lý HttpClient do nhầm lẫn, ví dụ:async Task<HttpResponseMessage> Method(){ using(var client = new HttpClient()) return client.GetAsync(request); }
JobaDiniz

3
Đối với những người sử dụng HttpClientnhư @JobaDiniz (với mộtusing() ), vui lòng dừng lại! Lý do: aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
Philippe

Câu trả lời:


272

Có 2 lý do có khả năng TaskCanceledExceptionsẽ bị ném:

  1. Một cái gì đó được gọi Cancel()trên CancellationTokenSourceliên kết với mã thông báo hủy trước khi nhiệm vụ hoàn thành.
  2. Yêu cầu đã hết thời gian, tức là không hoàn thành trong khoảng thời gian bạn đã chỉ định trên HttpClient.Timeout .

Tôi đoán đó là một thời gian chờ. (Nếu đó là một sự hủy bỏ rõ ràng, có lẽ bạn đã tìm ra điều đó.) Bạn có thể chắc chắn hơn bằng cách kiểm tra ngoại lệ:

try
{
    var response = task.Result;
}
catch (TaskCanceledException ex)
{
    // Check ex.CancellationToken.IsCancellationRequested here.
    // If false, it's pretty safe to assume it was a timeout.
}

3
Vì vậy, một giải pháp có thể là gì? Tôi có một vấn đề tương tự. stackoverflow.com/questions/36937328/ Mạnh
Nhà phát triển

48
@Dimi - cái này khá cũ, nhưng giải pháp tôi đã sử dụng là đặt thuộc tính Timeout thành giá trị lớn hơn:httpClient.Timeout = TimeSpan.FromMinutes(30)
RQDQ

2
@RQDQ, bạn là người đàn ông! Không sử dụng các nhà xây dựng đã giải quyết vấn đề cho tôi. Trong trường hợp cụ thể của tôi, tôi muốn thời gian chờ tính bằng mili giây. Sử dụng TimeSpan.FromMilliseconds(Configuration.HttpTimeout)trái ngược với new TimeSpan(Configuration.HttpTimeout)làm việc một điều trị. Cảm ơn!
Victor Ude

6
@RQDQ httpClient.Timeout = TimeSpan.FromMinutes(30)không phải là một cách tiếp cận tốt, bởi vì nó sẽ chặn luồng cụ thể đó trong 30 phút và cũng sẽ không đạt điểm cuối HTTP (đây là nhiệm vụ chính của bạn). Ngoài ra, nếu chương trình của bạn kết thúc trước 30 phút thì rất có thể bạn sẽ gặp phải ThreadAbortException. Một cách tiếp cận tốt hơn sẽ là tìm hiểu lý do tại sao điểm cuối HTTP không bị tấn công, nó có thể yêu cầu VPN hoặc một số truy cập mạng bị hạn chế.
Amit Upadhyay

6
@AmitUpadhyay Nếu cuộc gọi là awaited, thì không có luồng nào bị chặn. Không phải luồng UI, không phải luồng threadpool luồng khác, không.
Todd Menier

20

Tôi gặp phải vấn đề này vì Main()phương pháp của tôi không đợi nhiệm vụ hoàn thành trước khi quay lại, vì vậyTask<HttpResponseMessage> myTask đã bị hủy khi chương trình điều khiển của tôi thoát.

Giải pháp là gọi myTask.GetAwaiter().GetResult()vào Main()(từ câu trả lời này ).


9

Một khả năng khác là kết quả không được chờ đợi ở phía khách hàng. Điều này có thể xảy ra nếu bất kỳ một phương thức nào trong ngăn xếp cuộc gọi không sử dụng từ khóa await để chờ cuộc gọi hoàn thành.


7
var clientHttp = new HttpClient();
clientHttp.Timeout = TimeSpan.FromMinutes(30);

Trên đây là cách tiếp cận tốt nhất để chờ đợi trong một yêu cầu lớn. Bạn bối rối khoảng 30 phút; đó là thời gian ngẫu nhiên và bạn có thể đưa ra bất cứ lúc nào bạn muốn.

Nói cách khác, yêu cầu sẽ không đợi trong 30 phút nếu họ nhận được kết quả trước 30 phút. 30 phút có nghĩa là thời gian xử lý yêu cầu là 30 phút. Khi chúng tôi xảy ra lỗi "Nhiệm vụ đã bị hủy" hoặc yêu cầu yêu cầu dữ liệu lớn.


0

Một lý do khác có thể là nếu bạn đang chạy dịch vụ (API) và đặt điểm dừng trong dịch vụ (và mã của bạn bị kẹt ở một số điểm dừng (ví dụ: giải pháp Visual Studio đang hiển thị Gỡ lỗi thay vì Chạy )). và sau đó nhấn API từ mã máy khách. Vì vậy, nếu mã dịch vụ tạm dừng trên một số điểm dừng, bạn chỉ cần nhấn F5 trong VS.


0

Trong tình huống của tôi, phương thức điều khiển không được tạo thành async và phương thức được gọi bên trong phương thức điều khiển là không đồng bộ.

Vì vậy, tôi đoán rằng điều quan trọng là sử dụng async / đang chờ tất cả các cấp độ cao nhất để tránh các vấn đề như thế này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.