Lazy :“函数评估需要所有线程运行”
我有一个带有一些静态属性的静态类。 我在静态构造函数中初始化了所有这些,但后来意识到它是浪费的,我应该在需要时懒惰加载每个属性。 所以我切换到使用System.Lazy
类型来完成所有脏工作,并告诉它不要使用它的任何线程安全function,因为在我的情况下执行始终是单线程的。
我最后得到了以下课程:
public static class Queues { private static readonly Lazy g_Parser = new Lazy(() => new Queue(Config.ParserQueueName), false); private static readonly Lazy g_Distributor = new Lazy(() => new Queue(Config.DistributorQueueName), false); private static readonly Lazy g_ConsumerAdapter = new Lazy(() => 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; } } }
调试时,我注意到一条我从未见过的消息:
function评估需要运行所有线程
在使用Lazy
之前,直接显示了值。 现在,我需要单击带有线程图标的圆形按钮来评估延迟值。 这只发生在检索Lazy
的.Value
的属性上。 展开实际Lazy
对象的调试器可视化器节点时, Value
属性只显示null
,不带任何消息。
这条消息意味着什么,为什么它会在我的案例中显示出来?
我找到了一个MSDN页面标题为“ 如何:刷新监视值 ”解释它:
在调试器中计算表达式时,“值”列中可能会显示两个刷新图标之一。 一个刷新图标是一个包含两个箭头的圆圈,它们以相反的方向圈出。 另一个是一个圆圈,包含两条类似于线的波浪线。
…
如果出现两个线程,则由于潜在的跨线程依赖性而未评估表达式。 跨线程依赖性意味着评估代码需要应用程序中的其他线程临时运行。 处于中断模式时,应用程序中的所有线程通常都会停止。 允许其他线程临时运行会对程序的状态产生意外影响,并导致调试器忽略断点等事件。
如果有人能给出,我仍然想要更好的解释。 这个问题没有回答的问题包括:什么样的评估需要所有线程运行? 调试器如何识别这种情况? 单击线程刷新图标时会发生什么?
编辑:我认为我在ILSpy下检查Lazy
时偶然发现了答案(完全不同的原因)。 Value
属性的getter调用Debugger.NotifyOfCrossThreadDependency()
。 MSDN有这样的说法:
[…]执行function评估通常需要冻结除执行评估的线程之外的所有线程。 如果function评估需要在多个线程上执行(如远程处理方案中可能发生的话),则评估将阻止。 NotifyOfCrossThreadDependency通知通知调试器它必须释放线程或中止function评估。
所以基本上,为了防止你尝试评估某个表达式的烦人情况,Visual Studio只挂起30秒,然后通知你“函数评估已超时”,代码有机会通知调试器它必须解冻评估成功的其他线程,否则评估将永远阻止。
由于运行其他线程可能会中断调试会话,因为通常在评估表达式时,所有其他线程都会保持冻结状态,调试器不会自动执行并在让您跳下兔子洞之前发出警告。
我的猜测是调试器试图通过为您加载属性来避免影响应用程序状态。
您必须记住,只有在引用/访问属性时才会发生延迟加载。
现在,通常,您不希望调试影响应用程序的状态,否则将无法准确表示应用程序状态应该是什么( 想想multithreading应用程序和调试 )
看看Heisenbug
创建一个局部变量并为其指定要检查的值。
这将让您检查它,因为调试器不必担心访问属性是否会干扰您的应用程序,因为它在将其分配给局部变量时已经访问过它。
我挣扎了好几个小时,发现原始的错误消息,要求所有线程运行误导。 我从新解决方案访问现有数据库,并在新解决方案中创建新的Entity Framework
实体POCO
和数据访问层,以访问和映射到DB
。
我最初做了两件事。 我没有在我的C#实体POCO
正确定义主键,而我访问的table
在DB
有一个唯一的模式(它不是edi.tablename
而是edi.tablename
)。
在我的DbContext.cs
文件中,我执行了以下操作来映射正确模式下的表。 一旦我纠正了这些事情,错误消失了,它运作得很好。
protected override void OnModelCreating(DbModelBuilder dbModelBuilder) { base.OnModelCreating(dbModelBuilder); dbModelBuilder.Conventions.Remove(); dbModelBuilder.Entity().ToTable("TableName", schemaName: "EDI"); }
对我来说,我发现如果我有这个没关系this.Configuration.LazyLoadingEnabled = false;
或= true;
,如果我在DBContext中有这条线。 从我对这个问题的阅读中看起来似乎发生了,因为一个线程正在发生并且调试器需要运行它的权限/在它激发它之前警告你。 显然,在某些情况下,您甚至可以根据MUG4N的答案继续进行: 调试期间的Visual Studio:函数评估需要所有线程运行
但我发现的是我可以解决这个问题。
2个选项:
-
在
Queues
上添加.ToList()
:var q = db.Queues.OrderBy(e => e.Distributor).ToList();
-
我通过选择非公共成员> _internalQuery> ObjectQuery>结果视图找到了解决方法。