处理“任务已取消”异常

时间:2018-07-30 12:30:46

标签: c# async-await

我的C#应用​​程序将文件上传到某个API,我正在使用多部分请求,即,我正在上传文件的json字符串和二进制连接,它适用于大多数文件,但很少有异常,我意味着我们尝试使用名为50MB.zip的文件 我遇到了例外情况:

  

任务被取消。 ::::: System.Threading.Tasks.TaskCanceledException:任务被取消。      在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)      在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)      在System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()

我的代码大致如下:

public async Task<Dictionary<string , string>> Upload(string filePath)
{   
    FileInfo fi = new FileInfo(FilePath);
    string jsonString="some json string";
    byte[] fileContents=File.ReadAllBytes(fi.FullName);
    Uri webService = new Uri(url);
    HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post , webService);
    requestMessage.Method = HttpMethod.Post;
    requestMessage.Headers.Add("Authorization" , "MyKey1234");
    const string boundry = "------------------My-Boundary";
    MultipartFormDataContent multiPartContent = new MultipartFormDataContent(boundry);
    ByteArrayContent byteArrayContent = new ByteArrayContent(fileContents);
    multiPartContent.Add(byteArrayContent);
    requestMessage.Content = multiPartContent;
    HttpClient httpClient = new HttpClient();
    HttpResponseMessage httpResponse = await httpClient.SendAsync(requestMessage , HttpCompletionOption.ResponseContentRead , CancellationToken.None);
    //exception in this line ^
    return new Dictionary<string , string>();
}

呼叫者:

myDictionary = await Upload(filePath);

控制台应用程序的结构如下:

class Program
{
    static void Main(string[] args)
    {
        MainAsync().Wait();
    }

    static async Task MainAsync()
    {
        new MyClass().Start();
    }
}

MyClass:

public async void Start()
{
    myDictionary = await Upload(filePath);
}

我想我没有正确使用异步,也许您能看到我所缺少的吗?有什么想法吗?

1 个答案:

答案 0 :(得分:2)

我有99%的把握确定此错误是由于超时引起的,或者您实际上并未等待Start中的MainAsync方法造成的事实

我已经解决了以下代码中的超时问题以及其他一些小的更改,这些更改不一定能回答您的问题,但是希望能对您有所帮助

class Program
{
    private static HttpClient httpClient;

    static void Main(string[] args)
    {
        httpClient = new HttpClient();
        httpClient.BaseAddress = new Uri("your base url");
        // add any default headers here also
        httpClient.Timeout = new TimeSpan(0, 2, 0); // 2 minute timeout

        MainAsync().Wait();
    }

    static async Task MainAsync()
    {
        await new MyClass(httpClient).StartAsync();
    }
}

我在这里所做的是将HttpClient从您的Upload()方法中移出,因为该类被设计为可以多次重用。我已经将httpClient对象传递给MyClass的构造函数,您将在下一个代码片段中看到它。

我也将MainAsync()的{​​{1}}更改为await(由于从后缀的异步方式开始习惯将它从Start改名为StartAsync),因为在您的原始代码StartAsync中实际上没有等待任何事情

正如我在评论中提到的,如果将MainAsync()更改为MainAsync().Wait(),则可以将await MainAsync()更改为Main,这将需要您将构建语言更改为C#7.1或更高版本

static async Task Main

public class MyClass { private Dictionary<string, string> myDictionary; private readonly HttpClient httpClient; public MyClass(HttpClient httpClient) { this.httpClient = httpClient; } public async Task StartAsync() { myDictionary = await UploadAsync("some file path"); } public async Task<Dictionary<string, string>> UploadAsync(string filePath) { byte[] fileContents; using (FileStream stream = File.Open(filePath, FileMode.Open)) { fileContents = new byte[stream.Length]; await stream.ReadAsync(fileContents, 0, (int)stream.Length); } HttpRequestMessage requestMessage = new HttpRequestMessage(); // your request stuff here HttpResponseMessage httpResponse = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead, CancellationToken.None); // parse response and return the dictionary } } 中,我做了以下更改

在类的构造函数中添加了一个MyClass参数,因此我们可以将全局HttpClient对象传递给该类,以供其重用(这是我们在HttpClient中所做的事情)

如前所述,我将MainAsync()Start重命名为UploadStartAsync,因为将异步方法后缀为Async是一个好习惯

UploadAsyncStart更改为void because you should only use async void for event handlers

我将文件的读取方式也更改为异步,因为拥有Task方法似乎很浪费,该方法随后阻塞了CPU等待async完成。尽可能将async / await用于I / O。