我知道 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在不同的主题上?
在不同的线程上执行相同的方法,这会在线程关联性很重要时引起问题吗?
答案 0 :(得分:3)
为什么A和B在不同的线程上?
首先,如果默认计划程序安排了Task
,那么无法保证Thread
将运行Task
。 AwaitTest()
的部分是分开执行的,所以不能保证它们会在同一个线程上运行。
其次,默认调度程序使用ThreadPool
来执行Task
。并且每个async
方法的第一部分同步运行。在您的情况下,这意味着AwaitTest()
的第一部分将在主线程上运行,第二部分将在某个ThreadPool
线程上运行。所以,你实际上保证他们不会在同一个线程上运行。
这会导致线程关联性很重要吗?
当然可以。但它在线程亲和力很重要的最常见情况下可以正常工作:GUI编程。这是因为GUI应用程序设置了SynchronizationContext
,这意味着如果async
方法的第一部分在UI线程上运行,第二部分也将在那里运行(除非您通过使用{{ 1}})。
但在其他情况下,它会引起问题。例如,请使用以下代码:
ConfigureAwait(false)
此代码在控制台应用程序中不起作用(Monitor.Enter(lockObject);
await someTask;
Monitor.Exit(lockObject);
很可能会抛出Exit()
),因为SynchronizationLockException
可以在与Exit()
不同的线程上运行。< / p>
答案 1 :(得分:1)
您已要求系统“等待”任务。您真正要问的是,调用await
的线程应该继续运行,await
之后的所有内容都是“延续”,在任务完成后将以异步方式运行。由于控制台应用程序中没有“消息泵”,因此没有简单的方法来编组回“主”线程,因此继续只是继续使用异步Task
的线程。如果您在WinForm或WPF应用程序中执行相同的测试,则继续将在UI线程上运行。
答案 2 :(得分:0)
This blog post很好地描述了TPL如何使用多个任务队列将工作窃取到现有线程池之上以获得最佳性能。