【译】StackExchange.Redis 中文文档(十三)Thread Theft

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

posted @ 2020-10-20 16:34  大杂草  阅读(717)  评论(1编辑  收藏  举报