当前位置: 首页 > 知识库问答 >
问题:

HttpClient.getAsync(...)使用await/async时从不返回

百里修真
2023-03-14

编辑:在测试用例5中,任务似乎处于waitingforactivation状态。

我在.NET 4.5中使用System.net.http.HttpClient时遇到了一些奇怪的行为--其中“等待”调用的结果(例如)HttpClient.getAsync(...)将永远不会返回。

只有在使用新的Async/Await语言功能和任务API的特定情况下才会出现这种情况--代码似乎总是在只使用延续时工作。

/api/test1
/api/test2
/api/test3
/api/test4
/api/test5 <--- never completes
/api/test6
public class BaseApiController : ApiController
{
    /// <summary>
    /// Retrieves data using continuations
    /// </summary>
    protected Task<string> Continuations_GetSomeDataAsync()
    {
        var httpClient = new HttpClient();

        var t = httpClient.GetAsync("http://stackoverflow.com", HttpCompletionOption.ResponseHeadersRead);

        return t.ContinueWith(t1 => t1.Result.Content.Headers.ToString());
    }

    /// <summary>
    /// Retrieves data using async/await
    /// </summary>
    protected async Task<string> AsyncAwait_GetSomeDataAsync()
    {
        var httpClient = new HttpClient();

        var result = await httpClient.GetAsync("http://stackoverflow.com", HttpCompletionOption.ResponseHeadersRead);

        return result.Content.Headers.ToString();
    }
}

public class Test1Controller : BaseApiController
{
    /// <summary>
    /// Handles task using Async/Await
    /// </summary>
    public async Task<string> Get()
    {
        var data = await Continuations_GetSomeDataAsync();

        return data;
    }
}

public class Test2Controller : BaseApiController
{
    /// <summary>
    /// Handles task by blocking the thread until the task completes
    /// </summary>
    public string Get()
    {
        var task = Continuations_GetSomeDataAsync();

        var data = task.GetAwaiter().GetResult();

        return data;
    }
}

public class Test3Controller : BaseApiController
{
    /// <summary>
    /// Passes the task back to the controller host
    /// </summary>
    public Task<string> Get()
    {
        return Continuations_GetSomeDataAsync();
    }
}

public class Test4Controller : BaseApiController
{
    /// <summary>
    /// Handles task using Async/Await
    /// </summary>
    public async Task<string> Get()
    {
        var data = await AsyncAwait_GetSomeDataAsync();

        return data;
    }
}

public class Test5Controller : BaseApiController
{
    /// <summary>
    /// Handles task by blocking the thread until the task completes
    /// </summary>
    public string Get()
    {
        var task = AsyncAwait_GetSomeDataAsync();

        var data = task.GetAwaiter().GetResult();

        return data;
    }
}

public class Test6Controller : BaseApiController
{
    /// <summary>
    /// Passes the task back to the controller host
    /// </summary>
    public Task<string> Get()
    {
        return AsyncAwait_GetSomeDataAsync();
    }
}

共有1个答案

沈飞跃
2023-03-14

您正在滥用API。

情况如下:在ASP.NET中,一次只能有一个线程处理一个请求。如果需要,您可以执行一些并行处理(从线程池中借用额外的线程),但只有一个线程具有请求上下文(其他线程不具有请求上下文)。

这由ASP.NETSynchronizationContext管理。

    null
  • (test1test2test3):continuations_getSomeDataAsync将继续调度到ASP.NET请求上下文之外的线程池中。这使得continuations_getSomeDataAsync返回的任务可以完成,而不必重新输入请求上下文。
  • (test4test6):由于等待任务,因此ASP.NET请求线程不会被阻塞。这允许AsyncaWait_GetSomeDataAsync在准备继续时使用ASP.NET请求上下文。

以下是最佳实践:

  1. 在“库”异步方法中,尽可能使用configureawait(false)。在您的示例中,这将AsyncaWait_getsomedataAsync更改为var result=await httpclient.getAsync(“http://stackoverflow.com”,httpcompletionoption.responseHeadersread).configureaWait(false);
  2. 不要阻止任务;它是异步。换句话说,使用await而不是getresult(task.resulttask.wait也应该替换为await)。

这样,您可以获得两个好处:continuation(AsyncaWait_GetSomeDataAsync方法的其余部分)在基本线程池线程上运行,该线程不必进入ASP.NET请求上下文;控制器本身是异步(它不会阻塞请求线程)。

更多信息:

  • 我的异步/等待介绍帖子,其中包括任务等待者如何使用synchronizationcontext的简要说明。
  • Async/Await FAQ,其中详细介绍了上下文。还请参见Await、UI和死锁!哦,天哪!即使您是在ASP.NET而不是UI中,这也适用于此处,因为ASP.NETSynchronizationContext将请求上下文限制为一次只能有一个线程。
  • 此MSDN论坛帖子。
  • Stephen Toub演示了这个死锁(使用UI),Lucian Wischik也演示了这个死锁。
 类似资料:
  • 所以我一直很高兴地使用async/await,因为Firebase云函数支持节点8。不过我有一件事要做。当使用可调用函数时,会告诉您必须在函数中返回promise,否则它将无法正常工作。当使用原始promise时,我很清楚如何使用它: 但是现在,随着异步等待,我不确定如何返回这个“promise链”: 有人知道吗?

  • 问题内容: 为了熟悉,我在Chrome中尝试了以下代码: 但不保存结果(字符串);而是持有一个需要再次等待的。这段代码确实给了我响应字符串: 如何使用await从函数返回实际的响应字符串? 问题答案: 要么 要么 这只是编写相同逻辑的另一种方法。

  • > < li >我在组织的租户azure环境中部署了一个ASP.NET(4.5)网站。 < li >它具有从网络位置(我们称之为nas驱动器)上传/下载/删除文件(任何类型)的功能,例如\nas8782\xyz\abc\ < li> 上传/下载/删除工作正常(参见下面的代码)。我们用的是CloudSdk。由我们的Azure团队创建的Azure库。 nasClient.UploadAsync nas

  • Async/await 是以更舒适的方式使用 promise 的一种特殊语法,同时它也非常易于理解和使用。 Async function 让我们以 async 这个关键字开始。它可以被放置在一个函数前面,如下所示: async function f() { return 1; } 在函数前面的 “async” 这个单词表达了一个简单的事情:即这个函数总是返回一个 promise。其他值将自动被

  • 在第一章节,我们简要介绍了async/.await,并用它来构建一个简单的服务器。本章将更为详细讨论async/.await的它如何工作以及如何async代码与传统的 Rust 程序不同。 async/.await是 Rust 语法的特殊部分,它使得可以 yield 对当前线程的控制而不是阻塞,从而允许在等待操作完成时,其他代码可以运行。 async有两种主要的使用方式:async fn和asyn

  • 用asyncio提供的@asyncio.coroutine可以把一个generator标记为coroutine类型,然后在coroutine内部用yield from调用另一个coroutine实现异步操作。 为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法async和await,可以让coroutine的代码更简洁易读。 请注意,async和await是针对coroutin