处理“任务被取消”异常

exe*_*exe 6 c# async-await

我的 C# 应用程序将文件上传到某个 API,我使用的是多部分请求,即我正在上传文件的 json 字符串和二进制内容,它适用于大多数文件,但对于极少数文件,它给出了例外,我的意思是让我们尝试对于名为50MB.zip 我的文件,出现异常:

A task was canceled. :::  ::: System.Threading.Tasks.TaskCanceledException: A task was canceled.
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
Run Code Online (Sandbox Code Playgroud)

我的代码大致如下:

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>();
}
Run Code Online (Sandbox Code Playgroud)

呼叫者,召集者:

myDictionary = await Upload(filePath);
Run Code Online (Sandbox Code Playgroud)

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

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

    static async Task MainAsync()
    {
        new MyClass().Start();
    }
}
Run Code Online (Sandbox Code Playgroud)

而里面 MyClass:

public async void Start()
{
    myDictionary = await Upload(filePath);
}
Run Code Online (Sandbox Code Playgroud)

我想我没有正确使用异步,你能看到我错过了什么吗?有任何想法吗?

p3t*_*tch 9

我 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

我也改MainAsync()awaitStartAsync(改名从开始到StartAsync,因为它的约定,以后缀异步方法),因为你原来的代码MainAsync()实际上并没有任何等待

正如我在评论中所提到的,您可以更改MainAsync().Wait()await MainAsync(),如果你改变Mainstatic async Task Main,这将需要您将构建语言更改为C#7.1或以上

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
    }
}
Run Code Online (Sandbox Code Playgroud)

MyClass我做了以下更改

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

如前所述,我已将Startand重命名UploadStartAsyncandUploadAsync因为将 async 方法后缀为 Async 是一种很好的做法

Start已从更改void为 aTask 因为您应该仅async void用于事件处理程序

我改变了你的文件读取方式也是异步的,因为有一种async方法会阻塞 CPU 等待File.ReadAllBytes完成似乎很浪费。在可能的情况下,您应该对 I/O 使用 async/await。