HttpClient - งานถูกยกเลิกหรือไม่


191

ทำงานได้ดีเมื่อมีงานหนึ่งหรือสองงาน แต่เกิดข้อผิดพลาด "งานถูกยกเลิก" เมื่อเรามีงานมากกว่าหนึ่งรายการ

ป้อนคำอธิบายรูปภาพที่นี่

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

ข้อยกเว้นภายในพูดว่าอย่างไร
RagtimeWilly

1
ทำไมคุณถึงใช้CancellationTokenพารามิเตอร์เป็นและไม่ใช้มัน?
จิม Aho

1
เหตุผลที่ฉันจัดการHttpClientโดยไม่ได้ตั้งใจเช่นasync Task<HttpResponseMessage> Method(){ using(var client = new HttpClient()) return client.GetAsync(request); }
JobaDiniz

3
สำหรับผู้ใช้HttpClientเช่น @JobaDiniz (มีusing()) โปรดหยุด! เหตุผล: aspnetmonsters.com 2016/08/2016-08-27
Philippe

คำตอบ:


274

มี 2 ​​สาเหตุที่TaskCanceledExceptionน่าจะเป็น:

  1. สิ่งที่เรียกว่าCancel()บนCancellationTokenSourceการเชื่อมโยงกับโทเค็นการยกเลิกก่อนงานเสร็จ
  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.
}

3
ดังนั้นทางออกที่เป็นไปได้คืออะไร? ฉันมีปัญหาที่คล้ายกัน stackoverflow.com/questions/36937328/…
นักพัฒนา

49
@Dimi - นี่ค่อนข้างเก่า แต่โซลูชันที่ฉันใช้คือการตั้งค่าคุณสมบัติการหมดเวลาเป็นค่าที่มากขึ้น:httpClient.Timeout = TimeSpan.FromMinutes(30)
RQDQ

2
@RQDQ คุณผู้ชายคน! การไม่ใช้ตัวสร้างแก้ไขปัญหาให้ฉัน ในกรณีเฉพาะของฉันฉันต้องการหมดเวลาเป็นมิลลิวินาที ใช้TimeSpan.FromMilliseconds(Configuration.HttpTimeout)เมื่อเทียบกับการnew TimeSpan(Configuration.HttpTimeout)ทำงานรักษา ขอบคุณ!
Victor Ude

6
@RQDQ httpClient.Timeout = TimeSpan.FromMinutes(30)ไม่ใช่วิธีที่ดีเพราะจะบล็อกเธรดนั้นเป็นเวลา 30 นาทีและจะไม่ไปถึงจุดสิ้นสุด HTTP (ซึ่งเป็นภารกิจหลักของคุณ) นอกจากนี้หากโปรแกรมของคุณเสร็จสิ้นก่อน 30 นาทีแสดงว่าคุณมีโอกาสที่จะประสบความThreadAbortExceptionสำเร็จมากที่สุด วิธีที่ดีกว่าคือการค้นหาสาเหตุที่จุดสิ้นสุด HTTP ไม่ได้รับความถูกต้องอาจต้องใช้ VPN หรือการเข้าถึงเครือข่ายที่ จำกัด
Amit Upadhyay

6
@AmitUpadhyay หากมีการโทรawaitแล้วจะไม่มีการบล็อกเธรด ไม่ใช่เธรด UI ไม่ใช่เธรดพูลเธรดเธรดพื้นหลังอื่น ๆ ไม่มี
Todd Menier

20

ฉันพบปัญหานี้เนื่องจากMain()วิธีการของฉันไม่ได้รอให้งานเสร็จก่อนกลับมาดังนั้นจึงTask<HttpResponseMessage> myTaskถูกยกเลิกเมื่อโปรแกรมคอนโซลของฉันออก

วิธีแก้ไขคือโทรmyTask.GetAwaiter().GetResult()เข้าMain()(จากคำตอบนี้ )


9

ความเป็นไปได้อีกอย่างหนึ่งคือผลลัพธ์ไม่ได้รออยู่ที่ฝั่งไคลเอ็นต์ กรณีนี้อาจเกิดขึ้นได้หากวิธีใดวิธีหนึ่งในสแตกการโทรไม่ได้ใช้คำสำคัญที่รอการรอให้การโทรเสร็จสมบูรณ์


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

ข้างต้นเป็นวิธีที่ดีที่สุดในการรอคำขอจำนวนมาก คุณสับสนประมาณ 30 นาที เป็นเวลาสุ่มและคุณสามารถให้เวลาที่คุณต้องการ

กล่าวอีกนัยหนึ่งคำขอจะไม่รอ 30 นาทีหากได้ผลลัพธ์ก่อน 30 นาที 30 นาทีหมายถึงเวลาดำเนินการตามคำขอคือ 30 นาที เมื่อเราเกิดข้อผิดพลาด "งานถูกยกเลิก" หรือข้อกำหนดการร้องขอข้อมูลขนาดใหญ่


0

อีกสาเหตุหนึ่งก็คือหากคุณใช้บริการ (API) และใส่เบรกพอยต์ในบริการ (และรหัสของคุณติดอยู่ที่เบรกพอยต์บางอย่าง (เช่นโซลูชัน Visual Studio แสดงการดีบักแทนการทำงาน )) จากนั้นกดปุ่ม API จากรหัสลูกค้า ดังนั้นหากรหัสบริการหยุดชั่วคราวในจุดพักบางอย่างคุณเพียงกด F5 ใน VS


0

ในสถานการณ์ของฉันวิธีการควบคุมไม่ได้ทำในฐานะ async และวิธีการที่เรียกว่าภายในวิธีการควบคุมนั้นเป็น async

ดังนั้นฉันเดาว่ามันสำคัญที่จะต้องใช้ async / รอจนถึงระดับสูงสุดเพื่อหลีกเลี่ยงปัญหาเช่นนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.