【问题标题】:What really happens when call async method?调用异步方法时真正发生了什么?
【发布时间】:2015-04-22 07:30:30
【问题描述】:

我试图理解为什么使用“异步”方法比使用简单的旧同步方法更好。

有一个小问题,我不明白。

关于同步方式:

  1. 我有一些线程调用方法FileStream.Read(...)
  2. 因为调用此方法是同步的,所以调用线程将等待,直到 IRP(I/O 请求包)发出此 Io 请求完成的信号。 直到 IRP 返回 ==> 该线程将暂停(睡眠)。

关于A-同步方式:

  1. 我有一些调用方法FileStream.ReadAsync(...)的线程(任务..让我们称这个线程'TheadAsync01')。
  2. 因为调用这个方法是A-Synchronous,所以调用线程不会等到IRP(I/O请求包)发出这个IO请求完成的信号;并且这个调用线程将继续他的下一个动作。

现在,当 IRP 发出这个 IO 请求完成的信号时,发生了什么?

(线程 TheadAsync01 现在正在做其他事情,无法继续使用 'FileStream.ReadAsync' 返回的内容。)

其他线程会以ReadAsync的返回值继续下一个动作吗?

我在这里不明白什么?

【问题讨论】:

  • A 的情况下,它不会继续下一条指令 - 它或多或少会注册一个回调,其结果将继续下一条指令并释放线程.当 IO 完成时,它将在新线程上调用回调并从那里继续

标签: c# .net async-await


【解决方案1】:

困扰你的原因是这个错误的假设:

线程 TheadAsync01 现在正在执行其他操作,无法继续 'FileStream.ReadAsync' 现在返回的工作。

在典型的应用程序中,I/O 是迄今为止最耗时的任务。

正确使用 TPL 时,线程不会被耗时的操作阻塞。相反,所有耗时的事情(换句话说,任何 I/O)都通过await 委派。因此,当您的 IRP 发出信号时,线程要么空闲,要么很快空闲。

如果有一些繁重的计算(一些耗时的,不是 I/O),您需要相应地计划,例如在专用线程上运行它。

【讨论】:

  • 当然你可以在线程上做一些耗时的工作(例如在做并行工作时——TPL中的P)——它是在等待 IO 时阻塞一个完整的线程只是一种耻辱(你看 - 在这种情况下会有 zero 工作)
  • @CarstenKönig 我认为调度程序将 IO 完成端口用于某些任务。
【解决方案2】:

函数 ReadAsync 立即返回一个值,即一个 Task 对象。您应该在某个地方对返回值做一些事情。规范的方法是使用 await:

await FileStream.ReadAsync(...)

这将确保调用站点在 ReadAsync 完成其工作之前不会继续操作。如果你想在此期间做一些事情,你可以稍后等待任务对象,或者你可以手动处理任务对象。

如果你只是调用 ReadAsync,忽略返回的任务对象,什么都不做,那么你的阅读大多是昂贵的空操作。

【讨论】:

    【解决方案3】:

    当 ***Async 方法返回 Task 或 Task 时,您可以使用它来跟踪异步操作的运行。您可以通过在任务上调用 .Wait() 使调用与调用代码同步运行。或者,从 .Net 4.5 开始,您可以等待任务。

    例如:

    private async void DoFileRead(...)
    {
        var result = await fileStream.ReadAsync(...);
        // Do follow on tasks
    }
    

    在这种情况下,任何后续代码都将被编译器包装在一个延续中,并在异步调用完成时执行。使用 async 关键字的一项要求是使用 async 关键字标记调用方法(参见上面的示例)。

    【讨论】:

    • 那么,返回的“任务”是新任务吗?这可能会导致创建一个新线程?
    • 是的。它由底层的 ReadAsync 调用创建
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-04
    • 1970-01-01
    • 2016-05-02
    • 1970-01-01
    • 1970-01-01
    • 2018-12-28
    • 2010-10-27
    相关资源
    最近更新 更多