簡體   English   中英

懶 <T> :“功能評估需要所有線程運行”

[英]Lazy<T>: “The function evaluation requires all threads to run”

我有一個帶有一些靜態屬性的靜態類。 我在靜態構造函數中初始化了所有這些,但后來意識到它是浪費的,我應該在需要時懶惰加載每個屬性。 所以我切換到使用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; } }
}

在調試時,我注意到了一條我從未見過的消息:

功能評估需要運行所有線程

在此輸入圖像描述

在使用Lazy<T>之前,直接顯示值。 現在,我需要單擊帶有線程圖標的圓形按鈕來評估惰性值。 這只發生在檢索Lazy<T>.Value的屬性上。 展開實際Lazy<T>對象的調試器可視化器節點時, Value屬性只顯示null ,不帶任何消息。

這條消息意味着什么,為什么它會在我的案例中顯示出來?

我找到了一個MSDN頁面標題為“ 如何:刷新監視值 ”解釋它:

在調試器中計算表達式時,“值”列中可能會顯示兩個刷新圖標之一。 一個刷新圖標是一個包含兩個箭頭的圓圈,它們以相反的方向圈出。 另一個是一個圓圈,包含兩條類似於線的波浪線。

...

如果出現兩個線程,則由於潛在的跨線程依賴性而未評估表達式。 跨線程依賴性意味着評估代碼需要應用程序中的其他線程臨時運行。 處於中斷模式時,應用程序中的所有線程通常都會停止。 允許其他線程臨時運行會對程序的狀態產生意外影響,並導致調試器忽略斷點等事件。

如果有人能提供,我仍然想要更好的解釋。 這個問題沒有回答的問題包括:什么樣的評估需要所有線程運行? 調試器如何識別這種情況? 單擊線程刷新圖標時會發生什么?

編輯:我認為在ILSpy下檢查Lazy<T>時我偶然發現了答案(完全不同的原因)。 Value屬性的getter調用Debugger.NotifyOfCrossThreadDependency() MSDN有這樣的說法:

[...]執行功能評估通常需要凍結除執行評估的線程之外的所有線程。 如果函數評估需要在多個線程上執行(如遠程方案中可能發生的那樣),則評估將阻止。 NotifyOfCrossThreadDependency通知通知調試器它必須釋放線程或中止功能評估。

所以基本上,為了防止你嘗試評估某個表達式的煩人情況,Visual Studio只掛起30秒,然后通知你“函數評估已經超時”,代碼有機會通知調試器它必須解凍評估成功的其他線程,否則評估將永遠阻止。

由於運行其他線程可能會破壞您的調試會話,因為通常在評估表達式時所有其他線程都保持凍結狀態,調試器不會自動執行並在讓您跳下兔子洞之前發出警告。

我的猜測是調試器試圖通過為您加載屬性來避免影響應用程序狀態。

您必須記住,只有在引用/訪問屬性時才會發生延遲加載。

現在,通常,您不希望調試影響應用程序的狀態,否則將無法准確表示應用程序狀態應該是什么( 想想多線程應用程序和調試

看看Heisenbug

創建一個局部變量並為其指定要檢查的值。

這將允許您檢查它,因為調試器不必擔心訪問屬性是否會干擾您的應用程序,因為它在將其分配給局部變量時已經訪問過它。

我掙扎了好幾個小時,發現原始的錯誤消息,要求所有線程運行誤導。 我從新解決方案訪問現有數據庫,並在新解決方案中創建新的Entity Framework實體POCO和數據訪問層,以訪問和映射到DB

我最初做了兩件事。 我沒有正確定義在我的C#實體的主鍵POCO ,並table ,我訪問過的一個獨特的模式DB (它不是dbo.tablenameedi.tablename )。

在我的DbContext.cs文件中,我執行了以下操作以在正確的架構下映射表。 一旦我糾正了這些事情,錯誤消失了,它運作得很好。

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

對我來說,我發現如果我有這個沒關系this.Configuration.LazyLoadingEnabled = false; = true; ,如果我在DBContext中有這條線。 從我對問題的解讀看來,似乎發生了,因為線程正在發生並且調試器需要運行它的權限/在它激發它之前警告你。 顯然,在某些情況下,您甚至可以根據MUG4N的答案繼續進行: 調試期間的Visual Studio:函數評估需要所有線程運行

但我發現的是我可以解決這個問題。

2個選項:

  1. Queues上添加.ToList()

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

  2. 我通過選擇非公共成員> _internalQuery> ObjectQuery>結果視圖找到了一種解決方法。

在此輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM