Другой вопрос о блокировке

Я пытаюсь заблокировать свое понимание многопоточности. Я изо всех сил стараюсь учить себя, но некоторые из этих вопросов нуждаются в разъяснении.

Я прошел три итерации с фрагментом кода, экспериментируя с блокировкой.

В этом коде единственное, что требует блокировки, - это this.managerThreadPriority.

Во-первых, простой процедурный подход с минималистичной блокировкой.

var managerThread = new Thread
(
    new ThreadStart(this.ManagerThreadEntryPoint)
);

lock (this.locker)
{
    managerThread.Priority = this.managerThreadPriority;
}

managerThread.Name = string.Format("Manager Thread ({0})", managerThread.GetHashCode());

managerThread.Start();

Затем один оператор для создания и запуска нового потока, но блокировка кажется слишком большой, чтобы включать создание и запуск потока. Компилятор каким-то волшебным образом не знает, что блокировка может быть снята после этого. Используется managerThreadPriority.

Я полагаю, что следует избегать такой наивной блокировки.

lock (this.locker)
{
    new Thread
    (
        new ThreadStart(this.ManagerThreadEntryPoint)
    )
    {
        Priority = this.managerThreadPriority,
        Name = string.Format("Manager Thread ({0})", GetHashCode())
    }
    .Start();
}

Наконец, один оператор для создания и запуска нового потока со «встроенной» блокировкой только вокруг общего поля.

new Thread
(
    new ThreadStart(this.ManagerThreadEntryPoint)
)
{
    Priority = new Func<ThreadPriorty>(() =>
    {
        lock (this.locker)
        {
            return this.managerThreadPriority;
        }
    })(),

    Name = string.Format("Manager Thread ({0})", GetHashCode())
}
.Start();

Хотите прокомментировать область действия операторов блокировки? Например, если мне нужно использовать поле в операторе if и это поле необходимо заблокировать, следует ли мне избегать блокировки всего оператора if? Например.

bool isDumb;

lock (this.locker) isDumb = this.FieldAccessibleByMultipleThreads;

if (isDumb) ...

Против.

lock (this.locker)
{
    if (this.FieldAccessibleByMultipleThreads) ...
}
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
259
3

Ответы 3

1) Прежде чем вы даже начнете другой поток, вам вообще не нужно беспокоиться о совместном доступе к нему.

2) Да, вам следует заблокировать доступ все к совместно используемым изменяемым данным. (Если он неизменяемый, блокировка не требуется.)

3) Не используйте GetHashCode () для указания идентификатора потока. Используйте Thread.ManagedThreadId. Я знаю, есть книги, которые рекомендуют Thread.GetHashCode (), но посмотрите документацию.

Нет необходимости блокировать что-либо до того, как вы запустите какие-либо потоки.

Если вы собираетесь только читать переменную, нет необходимости в блокировках. Когда вы смешиваете чтение и запись, вам нужно использовать мьютексы и аналогичную блокировку, и вам нужно заблокировать как поток чтения, так и поток записи.

Даже если вы читаете только в одном потоке и пишете в другом, вам все равно нужно заблокировать поток чтения - в противном случае нет гарантии, что вы когда-нибудь увидите только что записанные данные из другого потока.

Jon Skeet 01.10.2008 02:00

В этом случае вам все равно не обязательно нужна блокировка. Маркировка поля как изменчивого гарантирует, что читатель получит текущее значение.

Mark Bessey 01.10.2008 02:12

Да, это правда. Лично я предпочитаю избегать программирования без блокировок, если я не столкнулся с узким местом, потому что необходимо учитывать множество тонкостей, но вы правы в том, что это решает эту конкретную проблему.

Jon Skeet 01.10.2008 03:02

Care to comment about the scoping of lock statements? For example, if I need to use a field in an if statement and that field needs to be locked, should I avoid locking the entire if statement?

В общем, он должен быть ограничен той частью кода, для которой требуется охраняемый ресурс, и не более того. Это сделано для того, чтобы другие потоки могли использовать его как можно скорее.

Но это зависит от того, является ли блокируемый вами ресурс частью более широкой картины, которая должна поддерживать согласованность, или это автономный ресурс, не связанный напрямую с каким-либо другим.

Если у вас есть взаимосвязанные части, которые должны изменяться синхронно, весь этот набор частей должен быть заблокирован на время всего процесса. Если у вас есть независимый, единственный элемент, не связанный с чем-либо еще, то только этот один элемент должен быть заблокирован на время, достаточное для того, чтобы часть процесса могла получить к нему доступ.

Другими словами, вы защищаете синхронный или асинхронный доступ к ресурсу??

В целом синхронный доступ должен удерживать его дольше, потому что он заботится о более широкой картине, частью которой является ресурс. Он должен поддерживать согласованность со связанными ресурсами. В таком случае вы можете обернуть весь цикл for целиком, если хотите предотвратить прерывания, пока все не будут обработаны.

Асинхронный доступ должен удерживаться как можно короче. Таким образом, более подходящим местом для блокировки будет внутренняя часть кода, например, внутри цикла for или оператора if, чтобы вы могли освободить отдельные элементы сразу же, даже до того, как будут обработаны другие.


Помимо этих двух соображений, я бы добавил еще одно. Избегайте вложенности замков с участием двух разных запирающих объектов.. Я на собственном опыте убедился, что это вероятный источник взаимоблокировок, особенно если их используют другие части кода. Если два объекта являются частью группы, которую необходимо постоянно рассматривать как единое целое, такое вложение следует реорганизовать.

Другие вопросы по теме