.net 4.5 中的异步和同步之间的差异
本文关键字:同步 之间 异步 net | 更新日期: 2023-09-27 18:36:08
在我阅读有关 .Net 4.5 中的异步编程时,async
和await
关键字我在这里读了以下段落
处理异步请求
在 Web 应用程序中,在 启动或具有突发负载(并发性突然增加), 使这些 Web 服务调用异步将增加 应用程序的响应能力。异步请求需要 处理时间与同步请求相同。为 例如,如果请求发出需要两个 Web 服务调用 秒完成,请求需要两秒钟,无论它是 同步或异步执行。但是,在 异步调用,不会阻止线程响应其他线程 请求,同时等待第一个请求完成。因此 异步请求可防止请求排队和线程池增长 当有许多并发请求调用长时间运行时 操作。
对于粗体字,我无法理解异步请求处理所需的时间与同步请求相同?
例如:
public async Task MyMethod()
{
Task<int> longRunningTask = LongRunningOperation();
//indeed you can do independent to the int result work here
//and now we call await on the task
int result = await longRunningTask;
//use the result
Console.WriteLine(result);
}
public async Task<int> LongRunningOperation() // assume we return an int from this long running operation
{
await Task.Delay(1000); //1 seconds delay
return 1;
}
据我了解,LongRunningOperation()
从调用这里的第一行开始执行Task<int> longRunningTask = LongRunningOperation();
并在调用await
后返回值,所以从我的角度来看,异步代码比同步代码快,对吗?
另一个问题:
据我了解,正在执行的主线程MyMethod()
没有阻塞等待LongRunningOperation()
完成,但它返回到线程池以服务于另一个请求。 那么是否有另一个线程分配给LongRunningOperation();
来执行它呢?
如果是,那么异步编程和多线程编程有什么区别?
更新:
假设代码变成这样:
public async Task MyMethod()
{
Task<int> longRunningTask = LongRunningOperation();
//indeed you can do independent to the int result work here
DoIndependentWork();
//and now we call await on the task
int result = await longRunningTask;
//use the result
Console.WriteLine(result);
}
public async Task<int> LongRunningOperation() // assume we return an int from this long running operation
{
DoSomeWorkNeedsExecution();
await Task.Delay(1000); //1 seconds delay
return 1;
}
在这种情况下,LongRunningOperation()
会在执行过程中DoIndependentWork()
被另一个线程执行吗?
异步操作速度不快。如果您异步等待 10 秒(即 await Task.Delay(10000)
) 或同步(即 Thread.Sleep(10000)
) 这将需要相同的 10 秒。唯一的区别是第一个在等待时不会支撑线程,但第二个会。
现在,如果您启动一个任务并且不等待它立即完成,则可以使用相同的线程来执行其他工作,但它不会"加速"异步操作的运行:
var task = Task.Delay(10000);
// processing
await task; // will complete only after 10 seconds
关于你的第二个问题:Task.Delay
(像其他真正的异步操作一样)不需要执行线程,因此没有线程。 Task.Delay
是使用您启动的System.Threading.Timer
实现的,并在完成后引发一个事件,同时它不需要线程,因为没有要执行的代码。
因此,当正在运行的线程到达MyMethod
await longRunningTask
时,它就会被释放(只要longRunningTask
尚未完成)。如果它是一个ThreadPool
线程,它将返回到ThreadPool
,在那里它可以处理应用程序中的一些其他代码。
关于更新,流程将是这样:
-
MyMethod
开始处理 -
LongRunningOperation
开始处理 -
DoSomeWorkNeedsExecution
在调用线程上执行 - 在
LongRunningOperation
中达到await
,因此返回热任务。 -
DoIndependentWork
由同一个调用线程执行(LongRunningOperation
仍在"运行",不需要线程) MyMethod
达到await
。如果原始任务完成,则同一线程将同步进行,如果没有,则将返回最终将完成的热任务。
因此,您正在使用async-await
的事实允许您使用线程,否则该线程将被阻塞,等待同步等待执行的 CPU 密集型工作。
考虑以下两者之间的区别:
Thread.Sleep(1000);
和
await Task.Delay(1000);
两者都需要一秒钟才能运行。然而,在前一种情况下,当前线程将被阻止(并且其所有资源都无用),而在后一种情况下,当前线程可以做其他有用的事情(例如,为另一个请求提供服务)。
异步性不是要加快单个指令序列的速度,而是能够在同步代码阻塞时执行操作。
另一个问题
释放的线程将用于其他事项;在操作完成之前,不会分配任何线程。这是可能的,因为底层操作系统本身是异步的。在上面的示例中,使用了一个计时器,该计时器在线程空闲时发出信号,而不是线程停止用于内部。
(建立在I3arnon的答案之上)
总体而言,同步操作和使用async-await
的操作需要相同的时间,这并非绝对正确。
async-await
中涉及一些额外的逻辑。涉及对已完成的等待者和状态机的检查。这使得某些异步操作比相应的同步操作花费更多的时间。
另一方面,大多数适合async-await
的操作自然是异步的,并且涉及一些额外的处理以使其外观和感觉同步。在这些情况下,异步操作比同步操作花费的时间更少。
关于这个问题的引用与 Web 应用程序有关。对于 Web 应用程序,异步操作更多的是在可接受的时间内提供最大数量的请求,而不是为每个请求节省几微秒。另一方面,如果涉及上下文切换,最终会花费更多时间,这就是为什么在 Web 应用程序中使用 Task.Run
对应用程序弊大于利的原因。
如果您想了解有关async-awit
的更多信息,请阅读我的async-awit
策展上的文章。