3

我知道TPL 是面向任务的,而经典的线程模型是面向工人的。任务让您主要关注要解决的问题,而不是解决问题的机制。但是当谈到线程和任务的关系时,我仍然有点困惑。

下面是一个演示代码:

namespace AsyncUnderTheHood
{
    class Program
    {

        static void Main(string[] args)
        {
            Console.WriteLine("Main Start : {0}", Thread.CurrentThread.ManagedThreadId);
            AwaitTest();
            Console.WriteLine("Main End : {0}", Thread.CurrentThread.ManagedThreadId);
            Console.ReadLine();
        }

        public static void DoWork()
        {
            Console.WriteLine("DoWork Start: {0}", Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(5000);
            Console.WriteLine("DoWork End: {0}", Thread.CurrentThread.ManagedThreadId);
        }

        public async static void AwaitTest()
        {
            Console.WriteLine("AwaitTest Start : {0}", Thread.CurrentThread.ManagedThreadId);
            Task t = new Task(DoWork);
            t.Start();
            await t;
            Console.WriteLine("AwaitTest Done : {0}", Thread.CurrentThread.ManagedThreadId);
        }
    }
}

输出是这样的:

Main Start : 1
AwaitTest Start : 1   <------------ A
DoWork Start: 3
Main End : 1
DoWork End: 3
AwaitTest Done : 3    <------------ B

我的问题是,为什么 A 和 B 在不同的线程上?

在不同的线程上执行相同的方法,当线程亲和性很重要时,这会导致问题吗?

4

3 回答 3

3

为什么A和B在不同的线程上?

首先,如果您Task的 s 由默认调度程序调度,则无法保证哪个ThreadTask运行。并且各个部分AwaitTest()是分开执行的,所以不能保证它们会在同一个线程上运行。

其次,默认调度器使用sThreadPool来执行Task。并且每个方法的第一部分async同步运行。在您的情况下,这意味着第一部分AwaitTest()将在主线程上运行,第二部分将在某个ThreadPool线程上运行。因此,您实际上可以保证它们不会在同一个线程上运行。

当线程关联很重要时,这会导致问题吗?

它当然可以。但它会在线程亲和性很重要的最常见情况下正常工作:GUI 编程。这是因为 GUI 应用程序已经SynchronizationContext设置,这意味着如果async方法的第一部分在 UI 线程上运行,第二部分也将在那里运行(除非您使用 禁用它ConfigureAwait(false))。

但在其他情况下,它会导致问题。例如,采用以下代码:

Monitor.Enter(lockObject);
await someTask;
Monitor.Exit(lockObject);

此代码在控制台应用程序中不起作用(Exit()很可能会抛出SynchronizationLockException),因为它Exit()可以在与Enter().

于 2013-04-07T16:40:19.343 回答
1

您已要求系统“等待”一项任务。您真正要问的是,调用的线程await应该继续运行,并且之后的所有内容await都是“继续”,在任务完成后将异步运行。由于控制台应用程序中没有“消息泵”,因此没有简单的方法可以编组回“主”线程,因此继续使用 asynchronousTask的线程继续。如果您在 WinForm 或 WPF 应用程序中执行相同的测试,则延续将在 UI 线程上运行。

于 2013-04-07T16:35:02.937 回答
0

这篇博文很好地描述了 TPL 如何使用具有工作窃取功能的多个任务队列在现有线程池之上进行分层以获得最佳性能。

于 2013-04-07T18:09:21.797 回答