首页 文章

.net(dotnet)核心webapi和异步方法 - 需要反馈

提问于
浏览
0

我正在为PoC构建我的dotnet核心webapi服务,我担心为其他拱门/开发人员提供正确的指导 .

“相同”方法签名的变化很少

  • public dynamic Get(string name = _defaultName)

  • public IActionResult Get(string name = _defaultName)

  • public async Task Get(string name = _defaultName)

单独讨论为什么我使用动态而不是自定义类型,我试图理解差异和好处 .

请纠正我的(误)理解:

  • 无法控制http响应, Headers ,代码等 - 在成功或失败的情况下由框架设置,返回类型对运行时工具可见

  • 更好地控制http响应,但它会在执行方法时占用威胁,没有输出数据类型的定义

  • 更好地控制http响应,不会在执行方法时绑定威胁,但会分离另一个线程来计算结果,并且没有输出类型的定义

第3个选项的实现如下所示:

public async Task<IActionResult> Get(string name = _defaultName)
    {
        return await Task.Run(() => {
            dynamic response = new System.Dynamic.ExpandoObject();
            response.message = $"Hello {name}!";
            response.timestamp = DateTime.Now.ToUniversalTime();

            if (name.Equals(_defaultName, StringComparison.CurrentCultureIgnoreCase)) {
                response.hint = "Add forward slash and your name to the url, e.g.: hostname:port/my name";
            }

            return Ok(response);
        });
    }

我理解,对于HttpClient和EntityFramework等异步依赖,异步方法的实现已经存在 .

我想知道如果没有这样的下游异步依赖关系来追求异步webapi方法以及产生一些奇怪的代码是否有意义 .

1 回答

  • 4

    无法控制http响应, Headers ,代码等 - 在成功或失败的情况下由框架设置,返回类型对运行时工具可见

    你不应该使用动态,如果你需要从动作中返回任意响应,你可以使用

    public IActionResult Get(string name = _defaultName)
    {
        // or any other method which accepts a response object)
        return Ok(someModel);
    }
    

    更好地控制http响应,但它会在执行方法时占用线程,没有输出数据类型的定义

    请求线程将在动作是的持续时间内保持 . 但是,如果您不使用任何异步操作(DB Access,与另一个网站/ Web服务的网络连接或读/写文件到数据库),使用它就可以了 .

    // Here no async operations are called, so Task is not necessary
    public IActionResult Index(string name = _defaultName)
    {
        if(!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
    
        return Ok();
    }
    

    对于异步操作,您应该使用 Task<IActionResult> .

    更好地控制http响应,在执行方法时不会占用线程,但会分离另一个线程来计算结果,并且没有输出类型的定义

    这种假设是错误的 . 单独使用Task和真正的异步操作(I / O操作,如从文件系统读取,网络连接或访问数据库)将会成为一个新线程 . 该操作将启动并且线程返回到线程池 .

    将启动 No new thread . 当异步操作完成时,将从线程池中获取一个线程并继续操作 .

    // No extra thread is called and the request thread is only 
    // used in between "await" calls
    public async Task<IActionResult> Get(string name = _defaultName)
    {
        var result = await GetResultFromDatabase();
    
        return Ok(someModel);
    }
    

    请阅读Stephen Cleary关于MSDN的Async Programming : Introduction to Async/Await on ASP.NET文章 . 它是关于ASP.NET MVC 5(旧的webstack,而不是重写的ASP.NET Core),但原理仍然适用于ASP.NET Core .

    如果你有 CPU bound 操作(计算一些复杂的数学等),那么 do not start a new thread . 这将搞砸ASP.NET Core如何管理线程池,你将什么也得不到,但仍然会有上下文切换的开销,因此它会降低你的性能而不是增加它 .

    对于CPU绑定操作,只需在请求线程上删除__2020964_ .

    public IActionResult Get(string name = _defaultName)
    {
        // Don't await or run CPU intensive stuff via Task.Run
        var result = SomeCpuIntensiveOperation();
    
        return Ok(result);
    }
    

    您还可以混合CPU绑定和异步操作

    public async Task<IActionResult> Get(string name = _defaultName)
    {
        // runs async, no extra thread
        var valueFromDb = await GetValueFromDb();
    
        // Don't await or run CPU intensive stuff via Task.Run
        // runs sync, on request thread
        var result = SomeCpuIntensiveOperation(valueFromDb);
    
        return Ok(result);
    }
    

    我想知道没有这种下游异步依赖关系来追求异步webapi方法以及产生一些奇怪的代码是否有意义 .

    根据您对第三个假设的新知识,这是由您决定的

    通常,当您有比线程池中的可用线程更多的请求时,异步会更好地扩展 . 但是大多数小型应用程序从未达到过这种状态,特别是当它是学习练习或概念证明时 .

    除非你有一个处理100或1000个请求的高流量网站 per second ,否则你不太可能达到这个限制 .

相关问题