在C#中能用异步来包装同步调用吗?
我应该为同步方法公开异步包装器吗?
对于这样的问题,我的简短回答是“不”。
例如,考虑一个同步方法Sleep,它在N毫秒内不返回:
public void Sleep(int millisecondsTimeout)
{
Thread.Sleep(millisecondsTimeout);
}
现在,考虑创建异步版本的需求,这样返回的Task在N毫秒内不会完成。这里有一个可能的实现,简单地用Task包装Sleep。运行命令创建一个SleepAsync:
public Task SleepAsync(int millisecondsTimeout)
{
return Task.Run(() => Sleep(millisecondsTimeout));
}
这是另一个不使用Sleep的,而是重写实现以消耗更少的资源:
public Task SleepAsync(int millisecondsTimeout)
{
TaskCompletionSource<bool> tcs = null;
var t = new Timer(delegate { tcs.TrySetResult(true); }, null, –1, -1);
tcs = new TaskCompletionSource<bool>(t);
t.Change(millisecondsTimeout, -1);
return tcs.Task;
}
这两种实现都提供相同的基本行为,都在超时过期后完成返回的任务。然而,从可伸缩性的角度来看,后者更具可伸缩性。前一种实现在等待时间期间从线程池中消耗一个线程,而后一种实现仅依赖于一个有效的计时器,在等待时间到期时向Task发出信号。
场景
1.在服务器开发中,不要用异步方法包装同步方法,因为这只是转移了执行任务的线程,并不能带来实际性的性能提升,而且线程调度还会带来一定的开销
2.在桌面开发中,使用异步方法包装同步方法是可行的,因为某些操作会长期执行而阻塞UI。使用异步将UI线程转移到线程池执行,这点开销是值得的。
3.在桌面开发中,使用异步方法包装同步方法对并行性也很重要。 并行编程就是把一个问题分割成可以并发处理的子问题。 如果你将一个问题分割成子问题,然后串行地处理每个子问题,就没有任务并行可言,因为是在一个线程上处理。 相反,如果你通过异步调用将一个子问题转移到另一个线程,你就可以并发地处理这些子问题。 这样就可以通过包装来实现并行性。
4.平常我们在封装接口时,如果一个方法没有异步实现,就不要再去增加一个Task.Run包装的异步实现。因为这对调用者来说,并不友好。是否需要包装成异步,应该由调用者决定,我们直接给出同步实现的接口即可。
5. 好的异步,是可以充分利用服务器硬件资源,而不是换个方式阻塞
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix