当前位置: 首页 > 知识库问答 >
问题:

C编译器如何知道何时切断异步方法?

苏嘉志
2023-03-14

我试图了解更多关于异步/等待的信息,尤其是编译器如何知道在异步方法和等待时“暂停”,而不产生额外的线程。

例如,假设我有一个异步方法,如

DoSomeStuff();
await sqlConnection.OpenAsync();
DoSomeOtherStuff();

我知道等待sqlConnection。OpenAsync();是我的方法被“挂起”并且调用它的线程返回线程池的地方,一旦跟踪连接打开的任务完成,然后找到一个可用的线程来运行Do某物其他东西()

| DoSomeStuff() | "start" opening connection | ------------------------------------ | 
| ---------------------------------------------------------- | DoSomeOtherStuff() - |

这就是我困惑的地方。我查看了OpenAsync的源代码(https://referencesource.microsoft.com/#System.Data/System/Data/Common/DBConnection.cs,e9166ee1c5d11996,参考资料),并且

    public Task OpenAsync() {
        return OpenAsync(CancellationToken.None);
    }

    public virtual Task OpenAsync(CancellationToken cancellationToken) {
        TaskCompletionSource<object> taskCompletionSource = new TaskCompletionSource<object>();

        if (cancellationToken.IsCancellationRequested) {
            taskCompletionSource.SetCanceled();
        }
        else {
            try {
                Open();
                taskCompletionSource.SetResult(null);
            }
            catch (Exception e) {
                taskCompletionSource.SetException(e);
            }
        }

        return taskCompletionSource.Task;
    }

我想象看到编译器知道“切断”线程的某个地方,因为任务已经开始与外部资源通信,但我并没有真正看到这一点,事实上,Open();似乎暗示它正在同步等待。有人能解释一下这是如何成为无线程“真正的异步”代码的吗?

共有2个答案

厉令
2023-03-14

使用async await的好处在于调用以下函数的线程

await sqlConnection.OpenAsync();

将被释放,并且可以从所属的线程池中使用它。如果我们谈论的是ASP。NET应用程序线程将被释放,并可用于服务另一个传入的HTTP请求。这样ASP的线程。NET线程池将始终可用于服务HTTP请求,并且不会阻塞例如I/O,例如打开到数据库的连接并执行一些SQL语句。

更新

这里应该说明,如果您要等待的任务已经完成,那么您的代码将同步运行。

终子昂
2023-03-14

您的方法不一定会在等待时“挂起”。如果您等待的任务已经完成(您提供的代码中的情况),方法将照常继续。您正在查看的方法实际上不是SqlConnection所使用的方法,因为DbConnection是基类,而方法OpenAsync是虚拟的SqlConnection覆盖它,并提供真正的异步实现。然而,并不是所有的提供者都这样做,那个些不这样做的提供者确实会使用您在问题中展示的实现。

当使用这样的实现时——整个事情将同步运行,没有任何线程切换。假设你有

public async Task Do() {
    DoSomeStuff();
    await sqlConnection.OpenAsync();
    DoSomeOtherStuff();
}

并且您使用的提供程序不提供OpenAsync的真正异步版本。然后,当有人调用wait Do()时,调用线程将执行所有工作(DoSomeStuffOpenAsyncDoSomeOtherStuff)。如果这是UI线程,它将在整个过程中被阻止(当人们在UI线程中为此类提供者使用“异步”方法时,通常会发生这种情况,假设这会以某种方式将工作从UI线程中推迟,但这种情况不会发生)。

 类似资料:
  • 在这种情况下,编译器如何知道typename T将在其上定义一个方法?在Java中,我们可以指定泛型类扩展的接口,但C++显然没有相同的语法。那么,如果我们调用会发生什么呢?

  • Android片段的onresume/onpause方法与主机活动的生命周期紧密耦合,如图所示。 我想知道的是如何检测碎片从/返回到应用程序的导航流内部。 示例: 假设我有主要活动和片段A、B和C。 编辑: 澄清:我想知道从片段B(类似于活动与onPause和onResume的工作方式)

  • 我正在制作满足以下条件的Netty服务器: null 在我的情况下,既没有发生“通道不活动”事件,也没有发生“连接被对等方重置”异常。 这是我使用的Netty测试客户端代码的部分。 如何在要回复时注意到断开连接?

  • 问题内容: 我有以下引发ConcurrentModificationException的代码,因为我在同一列表上使用了两个不同的迭代器,其中一个正在修改列表。因此,第二个迭代器在读取列表时会引发异常,因为其他某个迭代器已经修改了列表。 我的问题是,如果尚未到达被删除的元素, 内部* 如何知道已被其他迭代器修改了?如何确定其他一些突变了?一种方法可能是跟踪大小,但这不是原因,因为其他迭代器可以替换任

  • 问题内容: 在最近的一次采访中,有人问我一个非常奇怪的问题。面试官问我如何仅使用编译器功能来计算1 + 2 + 3 + … + 1000。这意味着我不允许编写程序并执行它,但我只应该编写一个程序,该程序可以驱动编译器在编译时计算此和,并在编译完成时打印结果。作为提示,他告诉我,我可能会使用编译器的泛型和预处理器功能。可以使用C ++,C#或Java编译器。有任何想法吗??? 此问题与此处未询问任何