我的 Lazy<> 值工厂中的 InvalidOperationException

问题描述 投票:0回答:7

我有一个类包含如下内容:

public static class Config
{
    private static Lazy<ConfigSource> _cfgSrc = new Lazy<ConfigSource>(
        () => { /* "ValueFactory" here... */ },
        true);

    public static ConfigSource ConfigSource
    {
        get { return _cfgSrc.Value; }
    }
}

在访问

ConfigSource
属性时遇到了这个
InvalidOperationException

ValueFactory 试图访问此实例的 Value 属性。

我在访问

Value
属性的“价值工厂”方法中看不到任何内容。还有什么可以触发此异常吗?这个问题只会间歇性地发生,但一旦发生,就需要重置 IIS 来清除异常(一旦发生就似乎被缓存了)。

c# .net-4.0 lazy-evaluation
7个回答
39
投票

事实证明,此错误仅在尝试检查 Visual Studio 调试器中的

Value
Lazy<>
属性时发生。这样做似乎会造成死锁,因为访问
Value
似乎会使线程挂起很长时间,直到
InvalidOperationException
最终发生。我永远无法拦截原始
Exception
,所以我看不到内部堆栈跟踪。

我只是把它归结为 Visual Studio 中的错误或他们对

Lazy<>
的实现。


19
投票

循环依赖也发生在我身上,所以如果这些步骤让你无处可去,请尝试仔细检查堆栈跟踪并验证没有循环依赖。


16
投票

ValueFactory 试图访问此实例的 Value 属性。

它可能对某些人有帮助,我能够通过检查整个 ValueFactory 过程来修复该错误。 在我的示例中,我创建了一个简单的模型并将其与其他一些数据链接,但在链接过程中我访问了单例中的 Value 属性,这导致了错误。

所以 访问 ValueFactory 中的惰性对象的值会抛出这样的错误。 由于错误消息已经表明 ;-)


8
投票

Lazy<T>
的行为是缓存
ValueFactory
抛出的异常。由于
InvalidOperationException
消息中提供的信息不足,这可能会导致潜在的混淆行为。 Microsoft 通过 Connect 意识到了这个问题,但是,它被标记为 Wont Fix 因为他们认为异常本身有足够的信息来诊断问题。

如果您收到的 IOE 存在内部异常,它应该(不是说它会)包含足够的信息以继续前进。另一种可能性是你有一个

try...catch
块重新抛出异常(
throw ex;
而不是
throw;
),你将失去有价值的信息。


8
投票

要确保您的异常未被缓存,请使用 LazyThreadSafetyMode.PublicationOnly 作为第二个参数,而不是 true。

使用 true,你最终会得到一个 LazyThreadSafetyMode.ExecutionAndPublication。这将确保只有一个线程进入 ValueFactory 方法,同时也确保异常将被缓存。

  private static Lazy<ConfigSource> _cfgSrc = new Lazy<ConfigSource>(
        () => { /* "ValueFactory" here... */ },
        LazyThreadSafetyMode.PublicationOnly);

有关更多信息,请参阅提供的六字母变量链接。


0
投票

当延迟加载配置时,一定不要调用需要配置的方法。这将调用重新启动该过程的配置加载器,从而导致所描述的错误。

在我的例子中,我正在记录负载状态,而记录器需要配置


0
投票

这确实是由嵌套的

Lazy
实例引起的,当一个人在工厂中调用另一个
Value
但更具体地说,它是由这些
Lazy
实例的不同发布模式引起的,例如一个是
PublicationOnly
,另一个是
None
。我认为这实际上是 Lazy 实现中的一个错误。

[Test]
public async Task Test()
{
    var innerLazy = new Lazy<string>(() =>
    {
        Thread.Sleep(10);
        return "lazy";
    }, LazyThreadSafetyMode.None);
    var outerLazy = new Lazy<string>(() => innerLazy.Value, LazyThreadSafetyMode.PublicationOnly);

    var task1 = new Task<string>(() => outerLazy.Value);
    task1.Start();
    var task2 = new Task<string>(() => outerLazy.Value);
    task2.Start();

    await Task.WhenAll(task1, task2);

    Assert.That(outerLazy.Value, Is.EqualTo("lazy"));
}
© www.soinside.com 2019 - 2024. All rights reserved.