Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
объект
Заметка
Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Она включает предлагаемые изменения спецификации, а также информацию, необходимую на этапах проектирования и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию ECMA.
Может возникнуть некоторое несоответствие между спецификацией компонентов и завершенной реализацией. Различия отражены в соответствующих заметках собраний по проектированию языка (LDM) .
Дополнительные сведения о процессе внедрения спецификаций функций в стандарт языка C# см. в статье о спецификациях .
Проблема чемпиона: https://github.com/dotnet/csharplang/issues/7104
Сводка
Особый случай взаимодействия System.Threading.Lock с ключевым словом lock (вызов метода EnterScope неявно).
Добавьте предупреждения статического анализа, чтобы предотвратить случайное неправильное использование типа, где это возможно.
Мотивация
.NET 9 вводит новый тип System.Threading.Lock в качестве лучшей альтернативы существующей блокировке на основе монитора.
Наличие ключевого слова lock в C# может привести разработчиков к тому, что они могут использовать его с этим новым типом.
Это не будет блокироваться в соответствии с семантикой этого типа, но вместо этого будет рассматриваться как любой другой объект и будет использовать блокировку на основе монитора.
namespace System.Threading
{
public sealed class Lock
{
public void Enter();
public void Exit();
public Scope EnterScope();
public ref struct Scope
{
public void Dispose();
}
}
}
Подробный дизайн
Семантика инструкции блокировки (§13.13) изменяется, чтобы учитывать особый случай для типа System.Threading.Lock:
Инструкция
lockформыlock (x) { ... }
- , где
xявляется выражением типаSystem.Threading.Lock, точно эквивалентно:иusing (x.EnterScope()) { ... }System.Threading.Lockдолжны иметь следующую форму:namespace System.Threading { public sealed class Lock { public Scope EnterScope(); public ref struct Scope { public void Dispose(); } } }- где
x— это выражение типа ссылки , и это точно эквивалентно: [...]
Обратите внимание, что форма может не быть полностью проверена (например, не будет ошибок или предупреждений, если тип Lock не является sealed), но функция может не работать так, как ожидалось (например, не будет предупреждений, когда тип Lock преобразуется в производный, так как функция предполагает отсутствие производных типов).
Кроме того, новые предупреждения добавляются в неявные преобразования ссылок (§10.2.8) при переадресовии типа System.Threading.Lock:
Неявные преобразования ссылок:
- От любого reference_type до
objectиdynamic.
- Предупреждение сообщается, когда reference_type, как известно,
System.Threading.Lock.- От любого class_type
Sдо любого class_typeT, еслиSявляется производным отT.
- выдаётся предупреждение, когда
Sизвестно какSystem.Threading.Lock.- От любых class_type
Sк любым interface_typeT, при условии, чтоSреализуетT.
- выдаётся предупреждение, когда
Sизвестно какSystem.Threading.Lock.- [...]
object l = new System.Threading.Lock(); // warning
lock (l) { } // monitor-based locking is used here
Обратите внимание, что это предупреждение возникает даже для эквивалентных явных преобразований.
Компилятор не сообщает предупреждение в некоторых случаях, когда экземпляр не может быть заблокирован после преобразования в object:
- Если преобразование неявно и является частью вызова оператора равенства объектов.
var l = new System.Threading.Lock();
if (l != null) // no warning even though `l` is implicitly converted to `object` for `operator!=(object, object)`
// ...
Чтобы выйти из предупреждения и принудительного использования блокировки на основе монитора, можно использовать
- обычные средства подавления предупреждений (
#pragma warning disable) -
MonitorAPI напрямую, - неявное приведение, например
object AsObject<T>(T l) => (object)l;.
Альтернативы
Поддержка общего шаблона, который другие типы также могут использовать для взаимодействия с ключевым словом
lock. Это будущая работа, которая может быть реализована, когдаref structs может участвовать в универсальных шаблонах. Это обсуждалось в LDM 2023-12-04.Чтобы избежать неоднозначности между мониторной блокировкой и новыми элементами типа
Lock(или шаблоном в будущем), мы могли бы:- Введите новый синтаксис вместо повторного использования существующей инструкции
lock. - Требовать, чтобы новые типы блокировки были
struct(так как существующиеlockзапрещают типы значений). Могут возникнуть проблемы с конструкторами по умолчанию и копированием, если структуры имеют отложенную инициализацию.
- Введите новый синтаксис вместо повторного использования существующей инструкции
Генератор кода может быть защищен от завершения потоков (которые сами устарели).
Мы также могли бы предупредить, когда
Lockпередается в качестве параметра типа, потому что блокировка на параметре типа всегда использует блокировку на основе монитора.M(new Lock()); // could warn here void M<T>(T x) // (specifying `where T : Lock` makes no difference) { lock (x) { } // because this uses Monitor }Однако это может вызвать предупреждения при хранении
Lockв списке, что нежелательно.List<Lock> list = new(); list.Add(new Lock()); // would warn hereМожно включить статический анализ, чтобы предотвратить использование
System.Threading.Lockвusingиawait. То есть мы можем выдать ошибку или предупреждение для кода, например,using (lockVar.EnterScope()) { await ... }. В настоящее время это не требуется, так какLock.Scopeявляетсяref struct, поэтому код является незаконным в любом случае. Однако, если бы мы когда-либо разрешили использоватьref structв методахasyncили изменилиLock.Scope, чтобы он не былref struct, этот анализ стал бы полезным. (Мы также, вероятно, должны рассмотреть для этого все типы блокировки, соответствующие общему шаблону, в случае если они будут реализованы в будущем. Хотя может возникнуть необходимость в механизме отказа, поскольку некоторые типы блокировок могут быть разрешены для использования сawait.) Кроме того, это может быть реализовано в виде анализатора, включенного в среду выполнения.Мы могли бы ослабить ограничение, согласно которому типы значений не могут быть
locked.- для нового типа
Lock(только если предложение API изменило его сclassнаstruct), - для общего шаблона, в котором при реализации в будущем сможет участвовать любой тип.
- для нового типа
Мы могли бы разрешить новый
lockв методахasync, гдеawaitне используется внутриlock.- В настоящее время, так как
lockснижается доusingсref structв качестве ресурса, это приводит к ошибке во время компиляции. Решение заключается в извлеченииlockв отдельный метод, не относящийся кasync. - Вместо использования
ref struct Scopeмы могли бы выпустить методыLock.EnterиLock.Exitвtry/finally. Однако методExitдолжен вызываться при вызове из другого потока, отличного отEnter, поэтому он содержит подстановку потока, которая избегается при использованииScope. - Лучше всего разрешить компиляцию
usingнаref structв методахasync, если в телеawaitотсутствуетusing.
- В настоящее время, так как
Совещания по проектированию
-
LDM 2023-05-01: первоначальное решение о поддержке шаблона
lock - LDM 2023-10-16: включено в рабочий набор для .NET 9
-
LDM 2023-12-04: отклонил общий шаблон, приняв только специальные случаи для типа
Lockи добавление предупреждений статического анализа
C# feature specifications