我的控制台应用程序包含一个异步函数,以便在我的服务器上调用远程方法:
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停止工作,永远不会返回客户端。 我已经看到了建议使用异步方法来防止死锁的不同示例,但在我的情况下,我需要同步调用远程方法以维护事件的时间顺序,我不知道这是否是我的问题的原因
答案 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(..)