Lazy:“函数求值需要所有线程运行”

发布于 2024-10-07 13:52:24 字数 1323 浏览 5 评论 0 原文

我有一个带有一些静态属性的静态类。我在静态构造函数中初始化了所有这些属性,但后来意识到这是浪费,我应该在需要时延迟加载每个属性。因此,我转而使用 System.Lazy 类型来完成所有脏工作,并告诉它不要使用任何线程安全功能,因为在我的情况下执行始终是单线程的。

我最终得到了以下课程:

public static class Queues
{
    private static readonly Lazy<Queue> g_Parser = new Lazy<Queue>(() => new Queue(Config.ParserQueueName), false);
    private static readonly Lazy<Queue> g_Distributor = new Lazy<Queue>(() => new Queue(Config.DistributorQueueName), false);
    private static readonly Lazy<Queue> g_ConsumerAdapter = new Lazy<Queue>(() => new Queue(Config.ConsumerAdaptorQueueName), false);

    public static Queue Parser { get { return g_Parser.Value; } }
    public static Queue Distributor { get { return g_Distributor.Value; } }
    public static Queue ConsumerAdapter { get { return g_ConsumerAdapter.Value; } }
}

调试时,我注意到一条我从未见过的消息:

函数求值需要所有线程运行

enter image description here

在使用 Lazy 之前,直接显示数值。现在,我需要单击带有线程图标的圆形按钮来评估惰性值。这种情况仅发生在我正在检索 Lazy.Value 的属性上。当展开实际 Lazy 对象的调试器可视化工具节点时,Value 属性仅显示 null,而不显示任何消息。

该消息是什么意思?为什么它会显示在我的案例中?

I have a static class with some static properties. I initialized all of them in a static constructor, but then realized that it is wasteful and I should lazy-load each property when needed. So I switched to using the System.Lazy<T> type to do all the dirty work, and told it to not to use any of its thread safety features since in my case execution was always single threaded.

I ended up with the following class:

public static class Queues
{
    private static readonly Lazy<Queue> g_Parser = new Lazy<Queue>(() => new Queue(Config.ParserQueueName), false);
    private static readonly Lazy<Queue> g_Distributor = new Lazy<Queue>(() => new Queue(Config.DistributorQueueName), false);
    private static readonly Lazy<Queue> g_ConsumerAdapter = new Lazy<Queue>(() => new Queue(Config.ConsumerAdaptorQueueName), false);

    public static Queue Parser { get { return g_Parser.Value; } }
    public static Queue Distributor { get { return g_Distributor.Value; } }
    public static Queue ConsumerAdapter { get { return g_ConsumerAdapter.Value; } }
}

When debugging, I noticed a message I've never seen:

The function evaluation requires all threads to run

enter image description here

Before using Lazy<T>, the values were displayed directly. Now, I need to click on the round button with the threads icon to evaluate the lazy value. This happens only on my properties that are retrieving the .Value of Lazy<T>. When expanding the debugger visualizer node of the actual Lazy<T> object, the Value property simply displays null, without any message.

What does that message mean and why is it displayed in my case?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

夜空下最亮的亮点 2024-10-14 13:52:25

我为此苦苦挣扎了几个小时,发现有关要求所有线程运行的原始错误消息具有误导性。我从新解决方案访问现有数据库,并在新解决方案中创建新的实体框架实体 POCO 和数据访问层以访问和映射到数据库。

我最初做错了两件事。我没有在 C# 实体 POCO 中正确定义主键,并且我正在访问的 tableDB 中具有唯一的架构(它不是dbo.tablename而是edi.tablename)。

在我的 DbContext.cs 文件中,我执行了以下操作以将表映射到正确的架构下。一旦我纠正了这些事情,错误就消失了并且工作得很好。

protected override void OnModelCreating(DbModelBuilder dbModelBuilder)
{
    base.OnModelCreating(dbModelBuilder);
    dbModelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    dbModelBuilder.Entity<TableName>().ToTable("TableName", schemaName: "EDI");
}

I struggled with this for hours and found the original error message about requiring all threads to run misleading. I was accessing an existing database from a new solution and creating new Entity Framework entity POCOs and data access layers within the new solution to access and map to the DB.

I did two things initially wrong. I didn't properly define the primary key in my C# entity POCO, and the table I was accessing had a unique schema in the DB (it was not dbo.tablename but edi.tablename).

In my DbContext.cs file, I did the following to map the table under the right schema. Once I corrected these things the error went away and it worked just fine.

protected override void OnModelCreating(DbModelBuilder dbModelBuilder)
{
    base.OnModelCreating(dbModelBuilder);
    dbModelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    dbModelBuilder.Entity<TableName>().ToTable("TableName", schemaName: "EDI");
}
夏天碎花小短裙 2024-10-14 13:52:25

创建一个局部变量并将其分配给您要检查的值。

这将让您检查它,因为调试器不必担心访问该属性是否会干扰您的应用程序,因为在将其分配给局部变量时它已经访问了它。

Create a local variable and assign it the value you want to inspect.

This will let you inspect it because the debugger doesn't have to worry about whether or not accessing the property will disturb your application, because it will have already accessed it when assigning it to the local variable.

番薯 2024-10-14 13:52:25

对我来说,我发现是否有 this.Configuration.LazyLoadingEnabled = false;= true; 并不重要,无论我的 DBContext 中是否有该行。从我对这个问题的阅读来看,它似乎发生了,因为一个线程正在发生&调试器需要获得运行它的权限/在启动它之前警告您。显然,在某些情况下,您甚至可以允许它按照 MUG4N 的答案进行: 调试期间的 Visual Studio:函数评估需要所有线程运行

但我发现我可以解决这个问题。

2 个选项:

  1. 在您的队列上添加.ToList()

    var q = db.Queues.OrderBy(e => e.Distributor).ToList();

  2. 我通过选择“非公共”找到了解决方法会员> _内部查询>对象查询>结果视图。

输入图片此处描述

For me, I found it didn't matter if I had this.Configuration.LazyLoadingEnabled = false; or = true;, if I had the line in my DBContext or not. It seems to occur, from my reading up on the problem, because a thread is occurring & the debugger wants permission to run it/is warning you before it spurs it. Apparently, in some cases, you can even allow it to proceed according to MUG4N's answer here: Visual Studio during Debugging: The function evaluation requires all threads to run

But what I found was I could get around the issue.

2 options:

  1. Add .ToList() on your Queues:

    var q = db.Queues.OrderBy(e => e.Distributor).ToList();

  2. I found a workaround by selecting Non-Public Members > _internalQuery > ObjectQuery > Results View.

enter image description here

回眸一笑 2024-10-14 13:52:24

我找到了一个标题为“如何:刷新监视值的 MSDN 页面” 解释一下:

当您在调试器中计算表达式时,两个刷新图标之一可能会出现在“值”列中。一个刷新图标是一个包含两个箭头的圆圈,这两个箭头沿相反的方向旋转。另一个是一个圆圈,其中包含两条类似于螺纹的波浪线。

...

如果出现两个线程,则由于潜在的跨线程依赖关系,不会计算表达式。跨线程依赖意味着评估代码需要应用程序中的其他线程暂时运行。当您处于中断模式时,应用程序中的所有线程通常都会停止。允许其他线程暂时运行可能会对程序的状态产生意想不到的影响,并导致调试器忽略断点等事件。

如果有人可以给出的话,我仍然想要一个更好的解释。这没有回答的问题包括:什么样的评估需要所有线程运行?调试器如何识别这种情况?单击线程刷新图标时到底会发生什么?

编辑:我想我在检查ILSpy (出于完全不同的原因)。 Value 属性的 getter 调用了 Debugger.NotifyOfCrossThreadDependency()。 MSDN 是这么说的:

[...] 执行函数求值通常需要冻结除执行求值的线程之外的所有线程。如果函数求值需要在多个线程上执行(如远程处理场景中可能发生的情况),则求值将会阻塞。 NotifyOfCrossThreadDependency 通知通知调试器必须释放线程或中止函数评估。

因此,基本上,为了防止您尝试计算某些表达式而 Visual Studio 只是挂起 30 秒,然后通知您“函数计算已超时”的烦人情况,代码有机会通知调试器它必须解冻其他线程才能使评估成功,否则评估将永远阻塞。

由于运行其他线程可能会中断您的调试会话,通常当您计算表达式时,所有其他线程都保持冻结状态,因此调试器不会自动继续并在让您跳入兔子洞之前发出警告。

I've found an MSDN page titled "How to: Refresh Watch Values" explaining it:

When you evaluate an expression in the debugger, one of two refresh icons might appear in the Value column. One refresh icon is a circle that contains two arrows, which circle in opposite directions. The other is a circle that contains two wavy lines that resemble threads.

...

If the two threads appear, the expression was not evaluated because of a potential cross-thread dependency. A cross-thread dependency means that evaluating the code requires other threads in your application to run temporarily. When you are in break mode, all threads in your application are typically stopped. Allowing other threads to run temporarily can have unexpected effects on the state of your program and causes the debugger to ignore events such as breakpoints.

I'd still like a better explanation if anyone can give it. Questions that this doesn't answer include: What kind of evaluation requires all threads to run? How does the debugger identify such a case? What exactly happens when you click the thread refresh icon?

EDIT: I think I've stumbled across the answer when examining Lazy<T> under ILSpy (for a completely different reason). The getter of the Value property has a call to a Debugger.NotifyOfCrossThreadDependency(). MSDN has this to say:

[...] performing a function evaluation typically requires freezing all threads except for the thread that is performing the evaluation. If the function evaluation requires execution on more than one thread, as might occur in remoting scenarios, the evaluation will block. The NotifyOfCrossThreadDependency notification informs the debugger that it has to release a thread or abort the function evaluation.

So basically, to prevent the annoying case where you try to evaluate some expression and Visual Studio just hangs for 30 seconds and then informs you that "a function evaluation has timed out", the code has a chance to inform the debugger that it must unfreeze other threads for the evaluation to succeed or otherwise the evaluation will block forever.

Since running other threads may disrupt your debugging session, as usually when you evaluate an expression all other threads are kept frozen, the debugger doesn't automatically proceeed and warns you before letting you jump down the rabbit hole.

日裸衫吸 2024-10-14 13:52:24

我的猜测是调试器试图通过为您加载属性来避免影响应用程序状态。

您必须记住,延迟加载仅在您引用/访问属性时发生。

现在,一般来说,您不希望调试影响应用程序的状态,否则将无法准确表示应用程序的状态(考虑多线程应用程序和调试

)看看Heisenbug

My guess would be that the debugger is trying to avoid influencing the application state by loading the properties for you.

You have to remember, that lazy load only happens when you reference/access the properties.

Now, in general, you do not want debugging to affect the state of the application, otherwise that will not give an accurate representation of what the application state should be (Think multi threaded apllications and debugging)

Have a look at Heisenbug

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文