Lazy <t>:“函数评估需要所有线程运行”</t>

时间:2010-12-16 11:20:02

标签: c# visual-studio visual-studio-2010 lazy-loading visual-studio-debugging

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

我最终得到了以下课程:

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<T>之前,会直接显示这些值。现在,我需要单击带有线程图标的圆形按钮来评估惰性值。这只发生在检索.Value Lazy<T>的我的属性上。在展开实际Lazy<T>对象的调试器可视化器节点时,Value属性只显示null,而不显示任何消息。

该消息的含义是什么?为什么会显示在我的案例中?

5 个答案:

答案 0 :(得分:15)

我找到了一个标题为“How to: Refresh Watch Values”的MSDN页面解释它:

  

在调试器中计算表达式时,“值”列中可能会显示两个刷新图标之一。一个刷新图标是一个包含两个箭头的圆圈,它们以相反的方向圈出。另一个是一个圆圈,包含两条类似于线的波浪线。

     

...

     

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

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

编辑:我认为在ILSpy下审核Lazy<T>时我偶然发现了答案(完全不同的原因)。 Value属性的getter可以调用Debugger.NotifyOfCrossThreadDependency()。 MSDN有这样的说法:

  

[...]执行功能评估通常需要冻结除执行评估的线程之外的所有线程。如果函数评估需要在多个线程上执行(如远程方案中可能发生的那样),则评估将阻止。 NotifyOfCrossThreadDependency通知通知调试器它必须释放线程或中止功能评估。

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

由于运行其他线程可能会破坏您的调试会话,因为通常在评估表达式时,所有其他线程都会被冻结,调试器不会自动执行并在让您跳下兔子洞之前发出警告。

答案 1 :(得分:1)

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

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

现在,一般情况下,您不希望调试影响应用程序的状态,否则无法准确表示应用程序状态应该是什么(思考多线程应用程序和调试

查看Heisenbug

答案 2 :(得分:0)

创建一个局部变量并为其指定要检查的值。

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

答案 3 :(得分:0)

我在这个问题上挣扎了好几个小时,并找到了关于要求所有线程运行误导的原始错误消息。我正在从新解决方案访问现有数据库,并在新解决方案中创建新的Entity Framework实体POCO和数据访问层,以访问并映射到DB

我最初做了两件事。我没有在我的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");
}

答案 4 :(得分:0)

对我来说,我发现是否有this.Configuration.LazyLoadingEnabled = false;= true;都无关紧要,无论我的DBContext中是否包含该行。从我对问题的深入了解来看,这似乎是发生的,因为正在发生线程并且调试器需要运行它的权限/在警告之前警告您。显然,在某些情况下,您甚至可以根据MUG4N在这里的回答:Visual Studio during Debugging: The function evaluation requires all threads to run

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

2个选项:

  1. 在您的.ToList()上添加Queues

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

  2. 我找到了一种解决方法,方法是选择非公共成员> _internalQuery> ObjectQuery>结果视图。

enter image description here