当客户端应用程序托管在虚拟化服务器上​​时,PostAsync永远不会返回

时间:2015-03-24 15:37:39

标签: .net json asp.net-web-api c#-5.0

我的控制台应用程序包含一个异步函数,以便在我的服务器上调用远程方法:

public async Task<Int32> ApiCall(String Uri, Object Item)
{
    HttpResponseMessage response = new HttpResponseMessage();
    MediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();
    HttpContent content = new ObjectContent<Object>(Item, jsonFormatter);

    response = await Client.PostAsync(Uri,content).ConfigureAwait(false);

    if (response.IsSuccessStatusCode)
    {
        return await response.Content.ReadAsAsync<Int32>();
    }
    else if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
    {
        throw new MyException();
    }
    else
    {
        return -1;
    }
}

以这种方式定期(和同步)调用此方法:

public int ApiCallSync(MyObject Item)
{
    try
    {
        int result = ApiCall("<MethodName>", Item).Result;
        return result;
    }
    catch (AggregateException e)
    {
        return 0;
    }
    catch (Exception e)
    {
        return -1;
    }
}

除了由虚拟化Windows Server 2012 Datacenter(VMWare)组成的系统外,一切在不同环境(Windows Server 2012,VirtualBox上的Windows Server 2012,Windows Server 2008,Windows 7)上都能正常运行。在这个系统中,经过一段随机的时间后,PostAsync停止工作,永远不会返回客户端。 我已经看到了建议使用异步方法来防止死锁的不同示例,但在我的情况下,我需要同步调用远程方法以维护事件的时间顺序,我不知道这是否是我的问题的原因

3 个答案:

答案 0 :(得分:2)

这种行为可能是由上下文同步死锁引起的。为防止这种情况,请尝试将.ConfigureAwait(false)添加到ApiCall

中的所有异步调用中

在您的情况下,您应该在此行中添加该方法:

return await response.Content.ReadAsAsync<Int32>();

因此您应该将其更改为:

return await response.Content.ReadAsAsync<Int32>().ConfigureAwait(false);

您可以在该博客文章中阅读该问题的完整描述:http://blog.ciber.no/2014/05/19/using-task-configureawaitfalse-to-prevent-deadlocks-in-async-code/

答案 1 :(得分:1)

问题可能不是异步方法,而是与您尝试将异步调用转换为同步调用的方式有关。

在这个复杂的主题上,它们是Stackoverflow上的一些不错的线程。

因此,如果您真的无法将ApiCall设为异步方法,可以尝试以下方法:

当我需要同步调用异步方法时,一种安全且简单的方法就像微软在AspNet.Identity中所做的那样。

在他们的帮助程序的实现之下,您可以在版本2.0.0.0中的程序集Microsoft.AspNet.Identity.Core上找到它:

internal static class AsyncHelper
    {
        private static readonly TaskFactory _TaskFactory = new TaskFactory();

        public static void RunSync(Func<Task> func)
        {
            _TaskFactory.StartNew<Task>(func).Unwrap().GetAwaiter().GetResult();
        }

        public static TResult RunSync<TResult>(Func<Task<TResult>> func)
        {
            return _TaskFactory.StartNew<Task<TResult>>(func).Unwrap<TResult>().GetAwaiter().GetResult();
        }
    }

使用它:

var result = AsyncHelper.RunSync(() => ApiCall("<MethodName>", Item));

答案 2 :(得分:0)

尝试将ApiCallSync更改为

public Task<int> ApiCallSync(MyObject Item)
{
    try
    {
        int result = await ApiCall("<MethodName>", Item);
        return result;
    }
    catch (AggregateException e)
    {
        return 0;
    }
    catch (Exception e)
    {
        return -1;
    }
}

然后使用:

var result = await ApiCallSync(..)