C#中异步相关的问题

14 天前
 jmliubiao
get1 和 get2 接口的区别是一个加了 async 、await 一个没加的区别,加了 async 、await 会额外生成一些状态机相关的代码,除了这个区别还有其他区别吗?
我的理解是,如果不需要获取异步后的结果进行其他处理则可以不用加。如果不加 async 、await ,真到生产上会不会有什么问题?

示例代码:
using Microsoft.AspNetCore.Mvc;

namespace WebApplication1.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class TestController : ControllerBase
{
[HttpGet("get1")]
public Task<Student> Get1Async()
{
return new TsetService().Get1Async();
}
[HttpGet("get2")]
public async Task<Student> Get2Async()
{
return await new TsetService().Get2Async();

}
}


public class TsetService
{

public Task<Student> Get1Async()
{
// 模拟数据库查询
Task.Delay(100);
return Task.FromResult(new Student { Id = 1, Name = "张三" });
}

public async Task<Student> Get2Async()
{
// 模拟数据库查询
await Task.Delay(100);
return new Student { Id = 1, Name = "张三" };
}
}

public class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
}
594 次点击
所在节点    C#
11 条回复
netnr
14 天前
以前整理的 https://netnr.com/184

数据库查询,简单的说,不能提升单个请求性能,但能提升并发请求处理,QPS 提升
jmliubiao
14 天前
@netnr 你说的这些我都清楚,但是没有找到我想问的答案
geelaw
14 天前
楼主应该善待帮助自己的人,比如提供格式化之后的代码。

TestController 里面的代码没什么意思上的差别,Get1Async 没有额外包装,或许性能会好一些。

TsetService (维持楼主的错误拼写)里面则完全不同。

Get1Async 完全抛弃了 Task.Delay(100),所以 Get1Async 实际上没有任何延迟,return Task.FromResult 是同步执行的,await Get1Async() 也不会有延迟,因为被 await 对象是已经完成的 Task.FromResult 。

await Get2Async() 有 100ms 延迟。
geelaw
14 天前
@geelaw #3 所谓没有意思上的差别,是指假设有

Task<T> M();

那么

Task<T> M1() { return M(); }
async Task<T> M2() { return await M(); }

没什么区别,当然 TestController 里面 Get1Async, Get2Async 因为调用了 TsetService 里面不同的方法,实际的效果区别很大。
csys
14 天前
https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md?utm_source=csharpdigest&utm_medium&utm_campaign=286#prefer-asyncawait-over-directly-returning-task

简单来说,如果你的整个方法内部没有任何别的事情要做,就是单独一个 Task ,那么你直接 return 而非 await 这个 task ,可以避免 C#生成一个异步状态机,从而轻微避免了一些性能负担

我的经验里,这种场景非常少,实际上,一个方法的所有职责仅仅是为了调用另一个方法,本身就是一种代码坏味道
jmliubiao
14 天前
@geelaw 感谢你的回复,格式的问题不知道为什么发布后就变成这样了。
TsetService 里的方法我想表达是一个查询数据的动作。 类似这样:reutrn db.Set<Student>().FindAsync(x => x.Id == 1) 和 reutrn await db.Set<Student>().FindAsync(x => x.Id == 1) 。
jmliubiao
14 天前
@csys 是的,一个方法的所有职责仅仅是为了调用另一个方法的观点我认同。但现实的项目有不少这样的代码。有的有加 async 、await 返回,有的又没加。
geelaw
14 天前
@jmliubiao #6 oh wow 你应该看 #5 的信息,我记错了……

TestController 代码的效果只有无异常的情况下是一样的,考虑 new TsetService() 这个表达式产生异常的情况(比如内存不足,或者构造器抛出异常),那么 Get1Async 会同步得到异常,而 Get2Async 会把异常装进 Task<Student> 里面返回。

Task<int> M1(bool t) { if (t) throw new Exception(); return Task.FromResult(0); }
async Task<int> M2(bool t) { if (t) throw new Exception(); return 1; }

那么

try { M1(); } catch(Exception ex) { Console.WriteLine("caught M1"); }
M2(); Console.WriteLine("M2 succeeded");

的结果是

caught M1
M2 succeeded

因为 M2 的异常只有在 await 的时候才会浮现。但是 #5 说由于这个(异常总是异步的)优点所以应该尽量用 async/await 我就不知道了,我的感觉是最终使用的时候所有 Task 都要被 await ,此时包围之的方法必然是 async ,因此同步异常和异步异常最后都会浮现,时机不同,而且典型的代码里还会掺杂很多其他可能异常的语句,就看细抠异常语义是否有意义吧。
i8086
14 天前
这个例子问题其实不大。

如果 Get1Async() 方法 Task.Delay(100) 被替换为一组耗时的数据库操作。

asp.net 请求结束后,对象会被释放。
如果异步操作还没跑完,可能会引发 ObjectDisposedException 异常。
而且这么写,数据库操作这块出错,也可能抓取不到异常。
seleningchan1
13 天前
还有 C#的岗位吗
jmliubiao
13 天前
@seleningchan1 上位机还是有的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/1162642

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX