【问题标题】:What does it mean when an async method uses time on a thread?异步方法在线程上使用时间是什么意思?
【发布时间】:2014-05-15 05:48:33
【问题描述】:

来自MSDN

async 和 await 关键字不会导致额外的线程 创建。异步方法不需要多线程,因为异步 方法不在自己的线程上运行。该方法在当前运行 同步上下文并仅在线程上使用时间 方法处于活动状态。

我从“async 和 await 关键字不会导致创建额外的线程”中得出的结论是它只是使用已经从线程池创建的线程,这很好,但我难以理解的一点是“使用时间当方法处于活动状态时在线程上”。

这是否意味着当我在等待任务时,任务会“移动”到当前线程,还是将结果编组回当前线程?如果您查看输出,结果会在不同的线程 ID 上返回,但在它们全部完成后在原始线程上完成。我想我错过了这两个操作的“中间”。

void Main()
{   
    var tasks = new List<Task<byte[]>>();

    Console.WriteLine ("Starting on Managed Thread ID {0}, Background {1}, Pool {2}\n", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

    for (int i = 0; i < 5; i++)
    {
        tasks.Add(WriteFile(@"D:\Temp\" + i + ".txt", new String('*', i)));
    }

    Console.WriteLine ("Before Wait()ing on Managed Thread ID {0}, Background {1}, Pool {2}", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

    Task.WhenAll(tasks).Wait();

    Console.WriteLine ("Completed on Managed Thread ID {0}, Background {1}, Pool {2}", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);
}

async Task<byte[]> WriteFile(string path, string text)
{
    FileStream fs = new FileStream(path, FileMode.Create);

    try
    {   
        Console.WriteLine ("Writing to file " + path + " . Managed Thread ID {0}, Background {1}, Pool {2}", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

        var bytes = Encoding.Unicode.GetBytes(text);

        await fs.WriteAsync(bytes, 0, bytes.Length);

        Console.WriteLine ("Finished writing to file " + path  + ". Managed Thread ID {0}, Background {1}, Pool {2}", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

        return new byte[0];
    }
    finally
    {
        Console.WriteLine ("Finally! " + path + ". Managed Thread ID {0}, Background {1}, Pool {2}\n", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

        fs.Dispose();
    }
}

输出:

Starting on Managed Thread ID 46, Background True, Pool False

Writing to file D:\Temp\0.txt . Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\0.txt. Managed Thread ID 12, Background True, Pool True
Finally! D:\Temp\0.txt. Managed Thread ID 12, Background True, Pool True

Writing to file D:\Temp\1.txt . Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\1.txt. Managed Thread ID 12, Background True, Pool True
Finally! D:\Temp\1.txt. Managed Thread ID 12, Background True, Pool True

Writing to file D:\Temp\2.txt . Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\2.txt. Managed Thread ID 12, Background True, Pool True
Finally! D:\Temp\2.txt. Managed Thread ID 12, Background True, Pool True

Writing to file D:\Temp\3.txt . Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\3.txt. Managed Thread ID 24, Background True, Pool True
Finally! D:\Temp\3.txt. Managed Thread ID 24, Background True, Pool True

Writing to file D:\Temp\4.txt . Managed Thread ID 46, Background True, Pool False
Before Wait()ing on Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\4.txt. Managed Thread ID 18, Background True, Pool True
Finally! D:\Temp\4.txt. Managed Thread ID 18, Background True, Pool True

Completed on Managed Thread ID 46, Background True, Pool False

【问题讨论】:

    标签: c# asynchronous async-await


    【解决方案1】:

    我从“async 和 await 关键字不会导致创建额外的线程”的结论是它只是使用已经从线程池创建的线程

    不,这根本不是真的。 asyncawait 在语言级别根本不使用线程池。完全可以使用仅使用初始“​​主”线程的 async/await 编写程序。

    这并不意味着所有的事情都会发生在一个线程上——只是 async/await 不会强制创建额外的线程。编译器本身不会生成任何代码来执行此操作。这完全取决于您在等待什么。例如,您可以使用Task.Run 执行任务,这将(通常)使用线程池,您可以等待它。或者您可以等待调用Task.Delay 的结果,它本身不会启动任何线程,而只是安排任务在将来完成。或者您可以使用异步 IO,它可能使用 IO 完成端口,但实际上不会涉及另一个线程正在运行,大部分时间任务是“活动的”。

    精心设计的可等待对象(如果您使用任何内置的,这将包括您)将在与您开始等待时相同的上下文中安排延续(await 之后的所有代码) ,除非你明确告诉它不要这样做。该“上下文”可能意味着您回到同一个线程(例如在 UI 中)但可能不会(例如在线程池上下文中,延续运行在哪个线程池线程上并不重要)。

    在我看来,值得更多地了解异步在幕后的真正作用。这方面有很多资源,包括免费的和付费的:

    【讨论】:

    • 换句话说,如果我等待一个并非设计为在单独线程上运行的任务,那么整个操作将同步运行?
    • 我正在慢慢浏览这些博客。也许我应该在发布任何进一步的问题之前阅读它们。感谢您的帮助。
    • @VincePanuccio:这不是等待它——而是任务本身。等待只是等待任务完成,然后返回当前上下文。
    • 那么任务的实现是什么定义了周围代码异步或同步运行的能力?
    • @VincePanuccio:排序。如果Task 以某种方式强制自己在特定线程上运行,那显然会影响您在同一线程上运行的能力......但我认为我不会像您那样表达它。我怀疑更多的阅读会对你有所帮助:)
    猜你喜欢
    • 1970-01-01
    • 2023-03-21
    • 2014-06-02
    • 2011-03-24
    • 1970-01-01
    • 2012-04-11
    • 1970-01-01
    • 1970-01-01
    • 2013-11-26
    相关资源
    最近更新 更多