簡體   English   中英

HttpClient - 任務被取消?

[英]HttpClient - A task was cancelled?

當有一個或兩個任務時它工作正常,但是當我們列出多個任務時會拋出錯誤“任務被取消”。

在此處輸入圖片說明

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();
}

拋出TaskCanceledException可能原因有兩個:

  1. 在任務完成之前,在與取消標記關聯的CancellationTokenSource上調用Cancel()東西。
  2. 請求超時,即未在您在HttpClient.Timeout指定的時間跨度內完成。

我的猜測是超時。 (如果是顯式取消,您可能已經知道了。)您可以通過檢查異常來更加確定:

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.
}

我遇到了這個問題,因為我的Main()方法在返回之前沒有等待任務完成,所以當我的控制台程序退出時Task<HttpResponseMessage> myTask被取消。

C# ≥ 7.1

您可以使 main 方法異步並等待任務。

public static async Task Main(){
    Task<HttpResponseMessage> myTask = sendRequest(); // however you create the Task
    HttpResponseMessage response = await myTask;
    // process the response
}

C# < 7.1

解決方案是在Main()調用myTask.GetAwaiter().GetResult() Main() (來自這個答案)。

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

以上是等待大請求的最佳方法。 你有大約 30 分鍾的困惑; 這是隨機時間,你可以給任何你想要的時間。

換句話說,如果他們在 30 分鍾之前得到結果,請求將不會等待 30 分鍾。 30 分鍾表示請求處理時間為 30 分鍾。 當我們發生錯誤“任務被取消”,或大數據請求要求時。

另一種可能性是客戶端不等待結果。 如果調用堆棧上的任何一種方法不使用 await 關鍵字來等待調用完成,就會發生這種情況。

在我的 .net core 3.1 應用程序中,我遇到兩個問題,其中內部原因是超時異常。 1,一個是我收到聚合異常,它的內部異常是超時異常 2,其他情況是任務取消異常

我的解決方案是

catch (Exception ex)
            {
                if (ex.InnerException is TimeoutException)
                {
                    ex = ex.InnerException;
                }
                else if (ex is TaskCanceledException)
                {
                    if ((ex as TaskCanceledException).CancellationToken == null || (ex as TaskCanceledException).CancellationToken.IsCancellationRequested == false)
                    {
                        ex = new TimeoutException("Timeout occurred");
                    }
                }                
                Logger.Fatal(string.Format("Exception at calling {0} :{1}", url, ex.Message), ex);
            }

促進@JobaDiniz 對答案的評論:

不要做明顯的事情並處理HttpClient實例,即使代碼“看起來正確”:

async Task<HttpResponseMessage> Method() {
  using (var client = new HttpClient())
    return client.GetAsync(request);
}

C# 的新 RIAA 語法也是如此; 稍微不那么明顯:

async Task<HttpResponseMessage> Method() {
  using var client = new HttpClient();
  return client.GetAsync(request);
}

相反,為您的應用程序或庫緩存HttpClient的靜態實例, HttpClient用它:

static HttpClient client = new HttpClient();

async Task<HttpResponseMessage> Method() {
  return client.GetAsync(request);
}

Async()請求方法都是線程安全的。)

在我的情況下,控制器方法不是異步的,控制器方法內部調用的方法是異步的。

因此,我認為將 async/await 一直使用到頂級以避免此類問題很重要。

另一個原因可能是,如果您正在運行服務 (API) 並在服務中放置一個斷點(並且您的代碼卡在某個斷點處(例如,Visual Studio 解決方案顯示的是Debugging而不是Running ))。 然后從客戶端代碼點擊 API。 因此,如果服務代碼在某個斷點處暫停,您只需在 VS 中按 F5。

我知道為時已晚,但是我遇到了類似的問題,我使用了await和async,並且很快就解決了。

快樂的編碼。

我使用的是一個簡單的調用而不是async 一旦我添加了await並使方法async它就開始正常工作了。

public async Task<T> ExecuteScalarAsync<T>(string query, object parameter = null, CommandType commandType = CommandType.Text) where T : IConvertible
        {
            using (IDbConnection db = new SqlConnection(_con))
            {
                return await db.ExecuteScalarAsync<T>(query, parameter, null, null, commandType);
            }
        }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM