Thread Theft
如果你是因为异常中的链接来到这里,并且你只想让代码能正常运行,那么只要在 application startup 里添加以下代码:
ConnectionMultiplexer.SetFeatureFlag("preventthreadtheft", true);
看看是否能解决问题。如果您想了解更多有关此内容的信息,请继续阅读!
什么是 thread theft?
对于每个 redis 的连接,StackExchange.Redis 保留了我们已发送给 redis 的等待答复的命令队列。随着每个回复的到来,我们查看下一个挂起的命令(保留了顺序,这使事情变得简单),并为该回复触发 "here's your result" API。 对于 async
/await
代码,这将导致 "continuation" 被重新激活,这就是当 await
完成的任务完成后,代码恢复工作的方式。那是简单的版本,但现实有些微妙。
默认情况下,当你在 Task
上触发 TrySetResult
时,连续性将被同步调用。设置结果的线程现在可以立即继续运行,无论你想要执行何种连续性。 在我们的示例中,那将是非常糟糕的,因为那将意味着专用的读取器循环(这是要处理来自 redis 的结果)正在运行你的应用程序逻辑;这是 thread theft,并且会在信息中显示为带有 rs: CompletePendingMessage
的大量超时(rs
是 the reader state;你不应经常观察 它在 CompletePendingMessage*
步骤中显示,因为它的意思是非常快;如果你经常看到它,则可能意味着尝试设置结果时劫持了 reader。
为了避免这种情况,我们使用 TaskCreationOptions.RunContinuationsAsynchronously
标志。 它的作用在某种程度上取决于你是否具有 SynchronizationContext
。 如果没有(在控制台应用程序,服务等中很常见),则 TPL 使用标准线程池机制来安排继续。 如果有一个 SynchronizationContext
(在UI应用程序和Web服务器中很常见),则使用它的 Post
方法。 Post
方法意思着是一个异步调度 API。 但是并非所有实现都是平等的。 一些 SynchronizationContext
实现将 Post
视为同步调用。 对于 LegacyAspNetSynchronizationContext
尤其如此,如果使用以下方法配置 ASP.NET,则会得到以下结果:
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="false" />
or
<httpRuntime targetFramework="4.5" />
(citation)
在这些情况下,我们将再次导致 reader 被盗并用于处理你的应用程序逻辑。这可能使任何进一步的等待超时(无论是暂时的(直到应用程序逻辑选择释放线程),还是永久的(本质上使自己陷入僵局))都注定要失败。
为避免这种情况,本类库增加了一层怀疑;具体来说,如果启用了 preventthreadtheft
功能标志,我们将抢先地完成线程池中的任务队列。 这在默认情况下效率较低,但是当且仅当你的 SynchronizationContext
行为异常时,这既适当又必要,并且不代表额外的开销。
本类库将特别尝试检测 LegacyAspNetSynchronizationContext
,但这并不总是可靠的。 该标志也可用于其他类似情况的手动使用。
原文地址:Thread Theft