web-dev-qa-db-ja.com

Lazy <T>:「関数の評価にはすべてのスレッドが実行される必要があります」

いくつかの静的プロパティを持つ静的クラスがあります。それらすべてを静的コンストラクターで初期化しましたが、それは無駄であり、必要に応じて各プロパティを遅延ロードする必要があることに気付きました。そこで、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>を使用する前は、値が直接表示されていました。次に、スレッドアイコンの付いた丸いボタンをクリックして、遅延値を評価する必要があります。これは、.ValueLazy<T>を取得しているプロパティでのみ発生します。実際のLazy<T>オブジェクトのデバッガービジュアライザーノードを展開すると、Valueプロパティはメッセージなしで単にnullを表示します。

そのメッセージは何を意味し、私の場合はなぜ表示されるのですか?

18
Allon Guralnek

方法:ウォッチ値を更新する "」というタイトルのMSDNページを見つけました。

デバッガーで式を評価すると、2つの更新アイコンのいずれかが[値]列に表示される場合があります。 1つの更新アイコンは、反対方向に円を描く2つの矢印を含む円です。もう1つは、糸に似た2本の波線を含む円です。

.。

2つのスレッドが表示される場合、スレッド間の依存関係の可能性があるため、式は評価されませんでした。クロススレッド依存関係とは、コードを評価するには、アプリケーション内の他のスレッドを一時的に実行する必要があることを意味します。ブレークモードの場合、通常、アプリケーション内のすべてのスレッドが停止します。他のスレッドの一時的な実行を許可すると、プログラムの状態に予期しない影響が及ぶ可能性があり、デバッガーがブレークポイントなどのイベントを無視する原因になります。

誰かがそれを与えることができれば、私はまだより良い説明が欲しいです。これが答えない質問は次のとおりです。どのような種類の評価ですべてのスレッドを実行する必要がありますか?デバッガーはどのようにしてそのようなケースを識別しますか?スレッド更新アイコンをクリックすると、正確にはどうなりますか?

編集:ILSpy の下で_Lazy<T>_を調べたときに答えに出くわしたと思います(完全に異なる理由)。 Valueプロパティのゲッターには、Debugger.NotifyOfCrossThreadDependency()への呼び出しがあります。 MSDNには次のように書かれています。

[...]関数評価を実行するには、通常、評価を実行しているスレッドを除くすべてのスレッドをフリーズする必要があります。リモーティングシナリオで発生する可能性があるように、関数評価で複数のスレッドでの実行が必要な場合、評価はブロックされます。 NotifyOfCrossThreadDependency通知は、スレッドを解放するか、関数の評価を中止する必要があることをデバッガーに通知します。

したがって、基本的に、ある式を評価しようとしてVisual Studioが30秒間ハングし、「関数の評価がタイムアウトした」と通知するという煩わしいケースを防ぐために、コードはデバッガーにフリーズを解除する必要があることを通知する機会があります。評価が成功するための他のスレッド、またはそうでない場合、評価は永久にブロックされます。

他のスレッドを実行するとデバッグセッションが中断する可能性があるため、通常、式を評価するときに他のすべてのスレッドがフリーズしたままになるため、デバッガーは自動的に処理されず、うさぎの穴を飛び降りる前に警告します。

15
Allon Guralnek

私はこれに何時間も苦労し、すべてのスレッドに誤解を招くような実行を要求することに関する元のエラーメッセージを見つけました。新しいソリューションから既存のデータベースにアクセスし、新しいEntity FrameworkエンティティPOCOsと、新しいソリューション内にDBにアクセスしてマップするデータアクセスレイヤーを作成していました。

私は最初に2つの間違ったことをしました。 C#エンティティPOCOで主キーを適切に定義していませんでした。また、アクセスしていたtableには、DBに一意のスキーマがありました(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");
}
1
Steelpaddy

私の推測では、デバッガーは、プロパティをロードすることによって、アプリケーションの状態に影響を与えないようにしようとしていると思います。

遅延読み込みは、プロパティを参照/アクセスしたときにのみ発生することを覚えておく必要があります。

さて、一般的に、デバッグがアプリケーションの状態に影響を与えることは望ましくありません。そうしないと、アプリケーションの状態がどうあるべきかを正確に表すことができません(マルチスレッドアプリケーションとデバッグ

見てください Heisenbug

1
Adriaan Stander

私にとっては、DBContextに行があるかどうかに関係なく、_this.Configuration.LazyLoadingEnabled = false;_または_= true;_があるかどうかは問題ではないことがわかりました。問題を読んだところ、スレッドが発生していて、デバッガーがスレッドを実行する許可を求めているか、スレッドが起動する前に警告しているため、発生しているようです。どうやら、場合によっては、ここでのMUG4Nの回答に従って続行することもできます: デバッグ中のVisual Studio:関数の評価ではすべてのスレッドを実行する必要があります

しかし、私が見つけたのは、問題を回避できるということでした。

2つのオプション:

  1. Queues.ToList()を追加します。

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

  2. 非公開メンバー> _internalQuery> ObjectQuery>結果ビューを選択することで回避策を見つけました。

enter image description here

0
vapcguy

ローカル変数を作成し、検査する値を割り当てます。

これにより、デバッガーはプロパティへのアクセスがアプリケーションに影響を与えるかどうかを心配する必要がないため、ローカル変数に割り当てるときに既にアクセスされているため、検査できます。

0
Matt Knowles