【问题标题】:What thread runs a Task's continuation if you don't await the task?如果您不等待任务,哪个线程运行任务的延续?
【发布时间】:2022-12-07 10:42:38
【问题描述】:

在使用 async、Task 和 await 时,我正在尝试围绕 C# 中的控制流进行思考。

我了解 promises 是如何工作的,并且从异步方法返回的 Task<> 最终将包含计算/IO/任何结果。

我想我明白,如果您显式等待该任务,那么当前线程将阻塞,直到该任务完成。我还认为这意味着返回 Task 的异步方法中的代码将在线程池中的线程上运行。

我不明白的是,如果我不“等待”异步方法返回的任务,会发生什么情况。在我看来,延续是在调用异步方法的原始线程上执行的,但我不知道控制权如何返回到该线程。

这是一个例子。这是我使用的 UniTask,它基本上是 Unity 的任务:

    public async UniTask ConnectAsync(Connection connection)
    {
        Debug.Log(Thread.CurrentThread.Name); -> this prints "Main Thread"
        // Close Any Old Connections
        await DisconnectAsync();

        // Default Address
        if (string.IsNullOrEmpty(connection.Address)) { connection.Address = "localhost:6379"; }

        // Connect
        ConfigurationOptions config = new()
        {
            EndPoints =
            {
                { connection.Address, connection.Port },
            },
            User = connection.Username,
            Password = connection.Password,
        };
        m_Connection = await ConnectionMultiplexer.ConnectAsync(config);

        // Create Graph Client
        m_Graph = new(m_Connection.GetDatabase());

        // Notify
        await Editor.Controller.OnConnect();
        Debug.Log(Thread.CurrentThread.Name); -> this prints "Main Thread"
    }

如果我调用此方法,然后忽略等待返回的任务 (UniTask),则两个 Debug.Log() 都会显示正在“主线程”(即 UI 线程)上执行。

没有等待这个任务,主线程如何能够返回到这个延续? C# 是否等到线程处于 Suspended/WaitSleepJoin 状态?我不知道有任何代码会让 UI 线程休眠,所以我不确定。我当然不会让用户界面休眠。

【问题讨论】:

    标签: c# unity3d


    【解决方案1】:

    当您调用一个异步方法并且不等待返回的 Task 或 UniTask 时,该方法仍将异步执行,但控制权将立即返回给调用者,而无需等待任务完成。这意味着返回 Task 或 UniTask 的方法中的代码将在线程池中的线程上执行,而调用方法中的其余代码将继续在原始线程上执行。

    在您提供的示例中,对 Debug.Log 的两次调用在“主线程”上执行,因为未等待 ConnectAsync 方法中的代码。相反,该方法是异步执行的,控制权会立即返回给调用者,而无需等待任务完成。这意味着 ConnectAsync 方法中的代码将在线程池中的线程上运行,而调用方中对 Debug.Log 的调用将在“主线程”上执行。

    如果希望调用线程等待任务完成,可以使用 await 关键字暂停调用方法的执行,直到任务完成。这将导致调用线程阻塞,直到任务完成,并且 ConnectAsync 方法的延续将在任务完成后在调用线程上执行。

    例如:

    public async UniTask ConnectAsync(Connection connection)
    {
        Debug.Log(Thread.CurrentThread.Name); -> this prints "Main Thread"
        // Close Any Old Connections
        await DisconnectAsync();
    
        // Default Address
        if (string.IsNullOrEmpty(connection.Address)) { connection.Address = "localhost:6379"; }
    
        // Connect
        ConfigurationOptions config = new()
        {
            EndPoints =
            {
                { connection.Address, connection.Port },
            },
            User = connection.Username,
            Password = connection.Password,
        };
        m_Connection = await ConnectionMultiplexer.ConnectAsync(config);
    
        // Create Graph Client
        m_Graph = new(m_Connection.GetDatabase());
    
        // Notify
        await Editor.Controller.OnConnect();
        Debug.Log(Thread.CurrentThread.Name); -> this prints "Main Thread"
    }
    
    // ...
    
    // Call the ConnectAsync method and wait for it to complete
    await ConnectAsync(connection);
    

    在此示例中,ConnectAsync 方法仍然异步执行,但调用代码使用 await 等待任务完成,然后再继续。这意味着 ConnectAsync 方法中的 Debug.Log 调用将在调用 ConnectAsync 的线程上执行,并且 ConnectAsync 方法的延续将在任务完成后在同一线程上执行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-19
      • 2014-08-17
      • 1970-01-01
      相关资源
      最近更新 更多