【问题标题】:Async programming control flow异步编程控制流
【发布时间】:2013-06-07 07:49:22
【问题描述】:

过去两天我一直在阅读有关 c# 异步方法的内容,据我了解,与线程 (threadpool.queueuserworkitem()) 不同,对异步方法的调用不会立即返回,它只会在被调用的方法遇到等待或完成(或异常)

请看下面的例子。

public partial class MainWindow : Window
{
    // . . .
    private async void startButton_Click(object sender, RoutedEventArgs e)
    {
        // ONE
        Task<int> getLengthTask = AccessTheWebAsync();

        // FOUR
        int contentLength = await getLengthTask;

        // SIX
        resultsTextBox.Text +=
            String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength);
    }


    async Task<int> AccessTheWebAsync()
    {
        // TWO
        HttpClient client = new HttpClient();
        Task<string> getStringTask =
            client.GetStringAsync("http://msdn.microsoft.com");

        // THREE                 
        string urlContents = await getStringTask;

        // FIVE
        return urlContents.Length;
    }
} 

据我所知,在上面的代码中,AccessTheWebAsync() 是同步调用的(即控制在调用时不会立即返回)。但在名为“THREE”的行中,运行时将返回控制权。

我的问题是:

  1. 运行时如何决定何时使用线程池或何时在调用线程中运行任务? (即执行代码而不返回控制)

  2. 以上代码中的哪些点会使用新线程(或线程池)?

  3. 如果AccessTheWebAsync() 执行了计算密集型操作,例如运行具有无数次迭代的循环,则控制权只会在循环完成后返回给调用者。对吗?

  4. 在异步函数中,有没有办法立即返回控制,然后在后台线程中继续工作? (就像我们调用 threadpool.queueuserworkitem())

  5. 调用不带 await 的异步方法(假设可能)与调用非异步方法一样吗?

【问题讨论】:

    标签: c# async-await


    【解决方案1】:

    首先有一点很重要:如果要等待的任务尚未完成,运行时只会在 await 上返回调用者。

    问题1:我这里引用MSDN:

    async 和 await 关键字不会导致创建额外的线程。异步方法不需要多线程,因为异步方法不在自己的线程上运行。

    Source

    问题 2:可能在 GetStringAsync 的实现中,但我们不知道,我们也不必知道。让我们知道GetStringAsync 在不阻塞我们的线程的情况下以某种方式得到它的结果就足够了。

    问题3:如果循环放在await关键字之前,是的。

    问题4:引用与之前相同的段落:

    您可以使用 Task.Run 将 CPU 密集型工作转移到后台线程

    您不需要 asyncawait 关键字。示例:

    private Task<int> DoSomethingAsync()
    {
        return Task.Run(() =>
        {
            // Do CPU heavy calculation here. It will be executed in the thread pool.
            return 1 + 1;
        });
    }
    

    问题 5:我再引用一遍

    async 方法通常包含一个或多个 await 运算符,但没有 await 表达式不会导致编译器错误。如果 async 方法不使用 await 运算符来标记暂停点,则该方法会像同步方法一样执行,尽管有 async 修饰符。编译器会针对此类方法发出警告。

    Source(与以前相同的页面,只是不同的部分)

    【讨论】:

      【解决方案2】:

      运行时如何决定何时使用线程池或何时在调用线程中运行任务?

      如果有一个同步上下文并且您没有明确指定您不想继续在该上下文上(通过使用类似await task.ConfigureAwait(false) 的东西),那么该方法将在捕获的上下文上恢复。在 UI 应用程序中,该上下文是 UI 线程,假设方法是从 UI 线程调用的。

      上述代码中的哪些点会使用新的线程(或线程池)?

      无处可去。除非您另外指定(见上文),否则await 会恢复捕获的上下文,在您的情况下是 UI 线程。

      如果AccessTheWebAsync() 做了一些计算密集型的事情,比如运行一个包含无数次迭代的循环,则控制权只会在循环完成后返回给调用者。对吗?

      是的,假设循环在第一个“异步”await 之前。 (如果awaited Task 已经完成,await 也可以是“同步的”。)

      在异步函数中,有没有办法立即返回控件,然后在后台线程中继续工作?

      您应该为此使用Task.Run()

      调用一个没有等待的异步方法(假设它是可能的)与调用非异步方法一样吗?

      这是可能的,编译器会警告你该方法将完全同步执行。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-06-10
        • 1970-01-01
        • 2022-01-03
        • 2013-07-12
        • 2019-09-14
        • 1970-01-01
        • 2012-11-02
        • 1970-01-01
        相关资源
        最近更新 更多