异步死锁?

8

我相当肯定在我的应用程序中创建了死锁,并且不确定如何解决此问题。我有一些移动部件,并且是新手asyncawait,请耐心等待。

我有一个客户端上传文件,如下所示:

public static async Task<string> UploadToService(HttpPostedFile file, string authCode, int id)
{
    var memoryStream = new MemoryStream();
    file.InputStream.CopyTo(memoryStream);

    var requestContent = new MultipartFormDataContent();
    var fileContent = new ByteArrayContent(memoryStream.ToArray());
    fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse(file.ContentType);
    requestContent.Add(fileContent, "file", file.FileName);

    using (var httpClient = new HttpClient())
    {
        httpClient.BaseAddress = new Uri(BaseUrl);
        httpClient.DefaultRequestHeaders.Accept.Clear();

        var message =
            await
                httpClient.PostAsync(
                    string.Format("Upload?authCode={0}&id={1}", authCode, id),
                    requestContent);

        return await message.Content.ReadAsStringAsync();
    }
}

接收文件的部分:

[HttpPost]
public Task<HttpResponseMessage> Upload(string authCode, int id)
{
    var request = Request;

    var provider = new CustomMultipartFormDataStreamProvider(root);

    var task =
        request.Content.ReadAsMultipartAsync(provider)
            .ContinueWith(o =>
            {
                // ...
                // Save file
                // ...

                return new HttpResponseMessage()
                {
                    Content = new StringContent("File uploaded successfully"),
                    StatusCode = HttpStatusCode.OK
                };
            });

    return task;
}

这一切都始于以下内容:
protected void Page_Load(object sender, EventArgs e)
{
    if (IsPostBack)
    {
        var file = HttpContext.Current.Request.Files[0];

        var response = UploadToService(file, hiddenAuthCode.Value, int.Parse(hiddenId.Value));
    }
}

除了 PostAsync 没有识别到返回的 task,一切似乎都正常。我可以看到 await ... PostAsync ... 任务的状态是 WaitingForActivation,但我不太确定这是什么意思(请记住,我对这些东西还很陌生)。我的文件保存在正确的位置,但应用程序从未识别出来来自我的服务的响应。
如果有人能指点我方向,将不胜感激。
1个回答

6
我认为问题在于您只是在Page_Load内以一种“点火并忘记”的方式调用UploadToService。请求处理在此任务完成之前就已经结束了。
您应该在这个WebForms页面中使用<%@ Page Async="true" ...>,并在web.config中包含<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
然后使用RegisterAsyncTask,代码将如下所示:
protected void Page_Load(object sender, EventArgs e)
{
    if (IsPostBack)
    {
        var file = HttpContext.Current.Request.Files[0];

        RegisterAsyncTask(new PageAsyncTask(() => UploadToService(file, 
            hiddenAuthCode.Value, int.Parse(hiddenId.Value))));
    }
}

顺便提一下,您可以改进这个部分:
file.InputStream.CopyTo(memoryStream);

像这样:

await file.InputStream.CopyToAsync(memoryStream);

ContinueWith替换为async/await

[HttpPost]
public async Task<HttpResponseMessage> Upload(string authCode, int id)
{
    var request = Request;

    var provider = new CustomMultipartFormDataStreamProvider(root);

    await request.Content.ReadAsMultipartAsync(provider);

    return new HttpResponseMessage()
    {
        Content = new StringContent("File uploaded successfully"),
        StatusCode = HttpStatusCode.OK
    };
}

相关: "在ASP.NET 4.5中使用异步方法".

该文章讲述了如何在ASP.NET 4.5中使用异步方法。

将此更改为RegisterAsyncTask后,我收到了参数类型System.Threading.Task.Task<string>无法分配给参数类型System.Web.UI.PageAsyncTask的错误。我尝试更改返回类型,但这会导致其他错误。有什么想法吗? - Joe
@Joe,再微调一下:new PageAsyncTask(() => UploadToService(file, ...)),参数是一个 Func<Task> - noseratio - open to work
1
感谢您!在进行了修改后,不再出现死锁。 - Joe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接