通过调用<asyncmethod> .Result实现同步方法

时间:2015-09-16 12:42:54

标签: c# asynchronous async-await task-parallel-library httpclient

我有这种异步方法:

public async Task<RES> PostAsync<RES>(string url, string content) where RES : new()
{
    using (var client = new HttpClient())
    {
        HttpResponseMessage message = await client.PostAsync(url, new StringContent(content, Encoding.UTF8, "application/json"));
        var readAsStringAsync = await message.Content.ReadAsStringAsync();
        return await readAsStringAsync.FromJsonAsync<RES>(mySerializerSettings);
    }
}

FromJsonAsync作为扩展方法实施的地方:

public async static Task<T> FromJsonAsync<T>(this string data, JsonSerializerSettings settings) where T : new()
{
    return (T)(await JsonConvert.DeserializeObjectAsync<T>(data, settings));
}

现在我想添加一个常规的同步Post方法,我认为实现将是:

public RES Post<RES>(string url, string content) where RES : new()
{
    return PostAsync<RES>(url, content).Result;
}

并不确实有效。我看到请求是通过Http嗅探器发送的,我收到了回复,但是在调试时我遇到了问题,无法继续。

顺便说一句,确实工作(使用Result代替await):

public RES Post<RES>(string url, string content) where RES : new()
{
    using (var client = new HttpClient())
    {
        HttpResponseMessage message = client.PostAsync(url, new StringContent(content, Encoding.UTF8, "application/json")).Result;
        var readAsStringAsync = message.Content.ReadAsStringAsync().Result;
        return readAsStringAsync.FromJson<RES>(mySerializerSettings);
    }
}

FromJson作为扩展方法实施的地方:

public static T FromJson<T>(this string data, JsonSerializerSettings settings) where T : new()
{
    return (T)JsonConvert.DeserializeObject<T>(data, settings);
}

该应用程序是一个Web后端(WebApi)。

我做错了什么?

3 个答案:

答案 0 :(得分:2)

你手上可能有僵局。

Asp.net使用SynchronizationContext将延续发布回请求上下文。如果上下文被阻止(就像在PostAsync<RES>(url, content).Result上那样),则无法执行继续,因此异步方法无法完成,并且您有死锁。

您可以使用ConfigureAwait(false)

来避免它
public async Task<RES> PostAsync<RES>(string url, string content) where RES : new()
{
    using (var client = new HttpClient())
    {
        HttpResponseMessage message = await client.PostAsync(url, new StringContent(content, Encoding.UTF8, "application/json"));
        var readAsStringAsync = await message.Content.ReadAsStringAsync().ConfigureAwait(false);
        return await readAsStringAsync.FromJsonAsync<RES>(mySerializerSettings).ConfigureAwait(false);
    }
}

但最好还是避免同步阻止异步代码开始,并为同步和异步提供两个不同的版本。

答案 1 :(得分:2)

虽然可能,但我不会使用@ i3arnon提供的答案。通常,您不应该阻止异步代码。尽管<script> var data=[ <?php $connection =mysql_connect("localhost","root","") or die ("we couldn't connect!"); mysql_select_db("testdatabase"); $query = mysql_query("CREATE OR REPLACE VIEW view1 AS SELECT value FROM testtable") or die(mysql_error()); $r2 = mysql_query("SELECT SUM(value) as value FROM view1;") or die(mysql_error()); while($row = mysql_fetch_array($r2)) { echo $row['value'].','; } ?>]; window.alert(data); </script> 确实有用,但它可能会导致您的代码库出现混淆,而其他开发人员也可能最终使用ConfigureAwait(false)进行阻止,而不使用.Result或了解其含义。

相反,公开真正同步的同步方法:

ConfigureAwait

答案 2 :(得分:0)

您似乎有一个非异步功能,并且您想要启动一个将调用PostAsync并等待此任务完成并返回任务结果的任务。这是你的问题吗?

  • 要启动任务,请使用Task.Run(()=&gt; ...);
  • 等待任务使用Task.Wait(...);
  • 查看任务是否因异常而停止:Task.IsFaulted
  • 任务的结果在Task.Result

您的代码可能是:

public async Task<RES> PostAsync<RES>(string url, string content) where RES : new()
{
    // start the task that will call PostAsync:
    var postTask = Task.Run( () => PostAsync(url, content));
    // while this task is running you can do other things
    // once you need the result: wait for the task to finish:
    postTask.Wait();
    // If needed check Task.IsFaulted / Task.IsCanceled etc. to check for errors

    // the returned value is in Task.Result:
    return postTask.Result;
}