Lazy <T>: "Для оценки функции требуется, чтобы все потоки выполнялись"
У меня есть статический класс с некоторыми статическими свойствами. Я инициализировал их все в статическом конструкторе, но потом понял, что это расточительно, и я должен lazy-load каждое свойство, когда это необходимо. Поэтому я переключился на использование типа 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>
. При расширении визуализатора отладки node фактического объекта Lazy<T>
свойство Value
просто отображает null
без какого-либо сообщения.
Что означает это сообщение и почему оно отображается в моем случае?
Ответы
Ответ 1
Я нашел страницу MSDN под названием " Как: Обновить значения часов", объясняя это:
Когда вы оцениваете выражение в отладчике, в столбце Значение может появиться один из двух значков обновления. Один значок обновления - это круг, содержащий две стрелки, которые пересекаются в противоположных направлениях. Другой - это круг, который содержит две волнистые линии, которые напоминают потоки.
...
Если появляются два потока, выражение не оценивалось из-за потенциальной зависимости между потоками. Зависимость между потоками означает, что для оценки кода требуется, чтобы другие потоки в вашем приложении выполнялись временно.. Когда вы находитесь в режиме прерывания, все потоки в вашем приложении обычно останавливаются. Разрешить запуск других потоков временно может привести к неожиданным последствиям для состояния вашей программы и заставляет отладчик игнорировать такие события, как точки останова.
Мне все же хотелось бы получить лучшее объяснение, если кто-нибудь сможет это дать. Вопросы, на которые это не отвечает, включают: Какая оценка требует, чтобы все потоки выполнялись? Как отладчик идентифицирует такой случай? Что именно происходит, когда вы нажимаете значок обновления потока?
EDIT: Я думаю, что наткнулся на ответ, рассматривая Lazy<T>
под ILSpy ( по совершенно другой причине). Получатель свойства Value
имеет вызов Debugger.NotifyOfCrossThreadDependency()
. MSDN говорит следующее:
[...] выполнение оценки функции обычно требует замораживания всех потоков, за исключением потока, который выполняет оценку. Если для оценки функции требуется выполнение более чем одного потока, как это может происходить в сценариях удаленных операций, оценка будет блокирована. Уведомление NotifyOfCrossThreadDependency сообщает отладчику, что он должен освободить поток или прервать оценку функции.
В основном, чтобы предотвратить раздражающий случай, когда вы пытаетесь оценить какое-то выражение, а Visual Studio просто зависает в течение 30 секунд, а затем информирует вас о том, что "функция оценки имеет тайм-аут", код имеет возможность сообщить отладчику, что он должен разморозить другие потоки для успешной оценки или иначе оценка будет заблокирована навсегда.
Так как запуск других потоков может привести к нарушению сеанса отладки, как обычно, когда вы оцениваете выражение, все остальные потоки остаются замороженными, отладчик автоматически не запускает и не предупреждает вас, прежде чем позволить вам спрыгнуть в кроличью нору.
Ответ 2
Я предполагаю, что отладчик пытается не влиять на состояние приложения, загружая свойства для вас.
Вы должны помнить, что ленивая нагрузка происходит только при ссылке/доступе к свойствам.
Теперь, в общем, вы не хотите, чтобы отладка влияла на состояние приложения, в противном случае это не даст точное представление о состоянии приложения (подумайте о многопоточных апликации и отладке)
Посмотрите Heisenbug
Ответ 3
Создайте локальную переменную и присвойте ей значение, которое вы хотите проверить.
Это позволит вам проверить его, потому что отладчику не нужно беспокоиться о том, будет ли доступ к свойству нарушать ваше приложение, потому что он уже обратился к нему при назначении его локальной переменной.
Ответ 4
Я боролся с этим в течение нескольких часов и нашел исходное сообщение об ошибке, требующее, чтобы все потоки запускались в заблуждение. Я обращался к существующей базе данных из нового решения и создавал новый объект Entity Framework
POCO
и уровни доступа к данным в новом решении для доступа и сопоставления с DB
.
Я сделал две вещи изначально неправильно. Я не правильно определил первичный ключ в моей сущности С# 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");
}