首先,什么是本地队列和全局队列?这是 .Net 4.0 中并行处理的优化。如果您有很多 small Task
s 并且只有一个全局队列,那么您会遇到很多争用。这是因为所有线程都Task
从同一个地方(全局队列的前面)处理 s,并且它们也将新Task
的 s 放到同一个地方(全局队列的后面)。这需要线程之间的大量同步,这会影响性能。
.Net 4.0 中的 TPL 使用了一种称为“工作窃取”的技术:和以前一样,有一个全局队列,每个ThreadPool
工作线程(但不是其他线程)也有一个本地队列。如果非工作线程启动 a Task
,它会像以前一样进入全局队列的后面。如果工作线程启动 a Task
,它将进入其本地队列的后部。
现在到有趣的部分。如果一个工作线程应该处理一个新的Task
,它会在这些地方寻找它(按这个顺序):
- 本地队列的尾部
- 全局队列的前面
- 其他线程的本地队列的前面
最后一部分是为什么这被称为“工作窃取”:工作线程可以“窃取” aTask
以从另一个线程进行处理。线程不需要使用同步来访问其本地队列的尾部,因为没有其他线程可以访问它。并且在本地以 LIFO 顺序处理Task
s 也有利于缓存,因为最后一个Task
(以及它使用的数据)最有可能仍在 CPU 缓存中。
有关这一切的另一种解释(带图片),请参阅.NET 4.0 中的 Work-Stealing。
这与内联和重入有什么关系?我不知道。如果 s 使用一些线程静态字段,我可以理解为什么会出现重入问题Task
,但这与队列无关。无论内联Task
来自哪个队列,都可能发生此问题。我想不出任何Task
可以保证本地队列中的 s 可以安全内联的情况,但Task
其他队列中的 s 可能不安全。