Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.80/25: Рейтинг темы: голосов - 25, средняя оценка - 4.80
Заблокирован

Потокобезопасность переменной

18.10.2020, 12:03. Показов 5732. Ответов 28
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Правильно ли я понимаю, что стандартным способом обеспечения потокобезопасности переменной является использование методов класса Interlocked (который делает ряд операций с переменной атомарными)?
Есть ли другие способы?
Например, можно ли обеспечить потокобезопасность свойства, защитив одним ключевым словом lock код и геттера и сеттера свойства?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
18.10.2020, 12:03
Ответы с готовыми решениями:

Потокобезопасность в C#
в общем вот есть такая структура данных, не будем вникать зачем мне она и что она делает, думаю тут и так понятно public class...

Потокобезопасность и прогресс
Здравствуйте, Помогите пожалуйста понять многопоточность и как сделать код потокобезопасным. Тема тяжело даётся. Можете на примерах кода...

Потокобезопасность uint
Всем привет. К примеру есть некий статический объект класса Player и он содержит: public uint PlayerZone; и есть к примеру 2...

28
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,109
Записей в блоге: 2
18.10.2020, 13:45
Цитата Сообщение от titan4ik Посмотреть сообщение
Например, можно ли обеспечить потокобезопасность свойства, защитив одним ключевым словом lock код и геттера и сеттера свойства?
lock - это оператор (инструкция). Операторы могут применяться только внутри методов.
Подобного модификатора насколько знаю нет.
Один из простых вариантов обеспечить потокозащищённость свойства:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private MyType _myProperty;
private readonly object _myPropertyLock = new object();
public MyType MyProperty
{
    get
    {
        lock(_myPropertyLock)
          return _myProperty;
 
     }
     set
     {
          lock(_myPropertyLock)
          {
               // Какой-то код
 
               _myProperty = value;
 
               // Какой-то код
          }
     }
}
1
Заблокирован
18.10.2020, 13:58  [ТС]
Элд Хасп,
а зачем модификатор readonly у формального поля _myPropertyLock?
P.S.
Кстати, почему всегда во всех примерах эти формальные поля никак больше не используются в коде? Нельзя ли для блокировки использовать не формальное, а реальное поле или свойство?
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,109
Записей в блоге: 2
18.10.2020, 14:09
Цитата Сообщение от titan4ik Посмотреть сообщение
а зачем модификатор readonly у формального поля _myPropertyLock?
Блокировка осуществляется не по переменной, а по ссылочному объекту хранящемуся в этой переменной.
Так как в методе, после получения из переменной ссылки на объект, контроля за состоянием переменной обеспечить нельзя, то может возникнуть ситуация когда между lock в разных потоках, значение этой переменой будет изменено.
И блокировка будет происходить по разным объектам.
Что приведёт к отсутствию взаимной блокировки.

Использование readonly гарантирует, что в этой переменной ссылка всегда на один и тот же объект и блокировка по нему всегда будет взаимной.
1
Заблокирован
18.10.2020, 14:15  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Использование readonly гарантирует, что в этой переменной ссылка всегда на один и тот же объект и блокировка по нему всегда будет взаимной.
То есть, всегда с lock использовать поле с модификатором readonly?
Но тогда ее и в коде можно использовать теоретически (не знаю зачем это может потребоваться)?
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,109
Записей в блоге: 2
18.10.2020, 14:18
Цитата Сообщение от titan4ik Посмотреть сообщение
Кстати, почему всегда во всех примерах эти формальные поля никак больше не используются в коде? Нельзя ли для блокировки использовать не формальное, а реальное поле или свойство?
Не всегда.
Но всегда надо гарантировать, что все lock из разных потоков обращаются к одному объекту.
Обращение к одному изменяемому полю (или свойству) этого гарантировать не может.

Пример потокозащищённого обращения к коллекции:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private readonly List<int> _numbers = new List<int>();
 
public IEnumerable<int> Numbers
{
     get
     {
           lock(_numbers)
             return _numbers.ToList().AsReadOnly(); 
     }
}
 
public void RemoveNumber(int number)
{
     lock(_numbers)
       _numbers.Remove(number);
}
 
public void AddNumber(int number)
{
     lock(_numbers)
       _numbers.Add(number);
}
Добавлено через 1 минуту
Цитата Сообщение от titan4ik Посмотреть сообщение
То есть, всегда с lock использовать поле с модификатором readonly?
Не всегда.
Но очень часто - можно сказать, практически всегда.

Добавлено через 21 секунду
Цитата Сообщение от titan4ik Посмотреть сообщение
Но тогда ее и в коде можно использовать теоретически (не знаю зачем это может потребоваться)?
Не понял вопроса.
2
Заблокирован
18.10.2020, 14:22  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Не всегда.
Но очень часто - можно сказать, практически всегда.
То есть, если не заморачиваться, то можно делать формальное поле типа object (или можно и int?) с модификатором readonly и все будет работать нормально. ОК.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,109
Записей в блоге: 2
18.10.2020, 14:31
Цитата Сообщение от titan4ik Посмотреть сообщение
То есть, если не заморачиваться.....
В простых случаях, когда одно-два свойства, редкие обращения к нему, сами обращения очень быстрые - да.

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

Допустим, если обращения на чтение частые, а потокозащищённость нужна только при записи/изменении объекта (поля, свойства), то можно рассмотреть вариант применения ReaderWriterLockSlim.

В других случаях могут потребоваться иные решения.
0
Заблокирован
18.10.2020, 14:49  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
надо понимать, что пока работает один lock, то остальные ждут и методы в которых они объявлены остановлены.
То есть lock приводит к торможению связанных методов.
Если это и есть цель - чтобы методы не работали, то всё ок. То есть, если цель в том, чтобы одновременно данный метод работал только в одном потоке.
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
18.10.2020, 14:58
Цитата Сообщение от titan4ik Посмотреть сообщение
стандартным способом обеспечения потокобезопасности переменной является использование методов класса Interlocked
Не стандартным, а продвинутым.
Стандартный способ — это lock.

Цитата Сообщение от titan4ik Посмотреть сообщение
Есть ли другие способы?
Масса.

Цитата Сообщение от titan4ik Посмотреть сообщение
а зачем модификатор readonly у формального поля _myPropertyLock?
Чтобы не было соблазна изменять переменную.
В принципе очень полезная привычка: по умолчанию поле объявлять как readonly и убирать этот модификатор только тогда, когда появится острая необходимость изменять его значение.

Цитата Сообщение от titan4ik Посмотреть сообщение
почему всегда во всех примерах эти формальные поля никак больше не используются в коде?
Потому что их специально создают для синхронизации.
Многопоточность — наверное, самая сложная часть в программировании, накосячить там — проще простого.
Потому лучше заранее всячески себя обезопасить, максимально изолировав любые объекты, используемые при синхронизации.

Цитата Сообщение от titan4ik Посмотреть сообщение
Нельзя ли для блокировки использовать не формальное, а реальное поле или свойство?
Можно, но не нужно.
Очень легко повесить намертво приложение.
Если объект используется для синхронизации, то нужно быть на 100% уверенным, что используется он только там, где его используете вы, т.е. иметь над ним полный контроль.
Если у вас какое-то поле, используемое для чего-то еще, но вы не можете быть уверены, что в реализации этого класса где-нибудь не прописано lock (this), а если доступ к значению этого поля есть у внешних классов, то тут вообще никаких гарантий и никакой уверенности.

Цитата Сообщение от Элд Хасп Посмотреть сообщение
всегда надо гарантировать, что все lock из разных потоков обращаются к одному объекту.
"Всегда" — сильно сказано, конечно.
Бывают ситуации, когда полезно лочиться на разных объектах в одном и том же методе. Например, когда есть группы объектов, между собой не связанные, но требующие синхронизации объектов в этой же группе. В этой ситуации хорошо иметь отдельный объект синхронизации для каждой группы.
3
Заблокирован
18.10.2020, 15:08  [ТС]
Вывод - если синхронизация участка кода обеспечивается с помощью lоck, то соответствующую переменную создаем формально, в коде не используем, лепим на неё "для надежности" модификатор readonly.
P.S.
Остался вопрос почему в примерах, как правило, используют для lock тип object? Придерживаться этого правила? Казалось бы, можно и int использовать, это не даст какую-нибудь экономию?
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,109
Записей в блоге: 2
18.10.2020, 15:12
Цитата Сообщение от kolorotur Посмотреть сообщение
"Всегда" — сильно сказано, конечно.
В данном случае я писал о ободном поле/свойстве, и пытался акцентировать на разнице между ссылкой на объект в переменной и самим объектом.

Добавлено через 1 минуту
Цитата Сообщение от titan4ik Посмотреть сообщение
Остался вопрос почему в примерах, как правило, используют для lock тип object?
Если от объекта нужна только блокировка, а её может дать только ссылочный тип, то object - это самый простой и, соответственно, наименее затратный тип.

Добавлено через 1 минуту
Цитата Сообщение от titan4ik Посмотреть сообщение
Вывод - если синхронизация обеспечивается с помощью lоck, то соответствующую переменную создаем формально, в коде не используем, лепим на неё "для надежности" модификатор readonly.
Не забываем - в простых случаях.
В реальности возможных разных вариантов очень много.
1
Заблокирован
18.10.2020, 15:19  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Пример потокозащищённого обращения к коллекции:
Прошу пардона за настырность.
Я правильно понимаю, что если бы в данном примере в качестве объекта для lock использовался бы некий формальный объект, то всё работало бы точно так же? То есть код ниже столь же хорош (и даже лучше, поскольку меньше шансов накосячить впоследствии):
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private readonly List<int> _numbers = new List<int>();
private readonly object _locker = new object();
public IEnumerable<int> Numbers
{
     get
     {
           lock(_locker)
             return _numbers.ToList().AsReadOnly(); 
     }
}
 
public void RemoveNumber(int number)
{
     lock(_locker)
       _numbers.Remove(number);
}
 
public void AddNumber(int number)
{
     lock(_locker)
       _numbers.Add(number);
}
Добавлено через 1 минуту
Цитата Сообщение от Элд Хасп Посмотреть сообщение
В данном случае я писал о ободном поле/свойстве,
"ободном" - это что?
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,109
Записей в блоге: 2
18.10.2020, 15:28
Цитата Сообщение от titan4ik Посмотреть сообщение
То есть код ниже столь же хорош (и даже лучше, поскольку меньше шансов накосячить впоследствии)
В таком применении да.
Но, допустим, у вас сложная логика, и есть статические методы по работе со с полем _numbers из разных экземпляров.
Метод просто получает коллекцию List<int> и не знает из какого она экземпляра.
В таком случае блокировку надо делать именно по самой коллекции полученной в параметрах.

Добавлено через 1 минуту
Цитата Сообщение от titan4ik Посмотреть сообщение
"ободном" - это что?
Описка:
В данном случае я писал об одном поле/свойстве,
1
Заблокирован
30.10.2020, 16:38  [ТС]
kolorotur,
мне представляется удобным использовать класс Interlocked для обеспечения потокобезопасности одной переменной.
Но его методы не определены явно для всех типов (только для ряда типов).
Какой метод Interlocked правильно использовать для перечислений?
Можно, конечно, вспомнить, что перечисление можно преобразовать в int и потом обратно и применить метод Exchange(Int32, Int32).
(хотя... сейчас пришло в голову, что наверное это может не получиться, ведь при этом возникнут прорехи в потокобезопасности в моменты преоразования из типа int в тип перечисления???)
Но можно ли использовать Interlocked для перечислений без необходимости преобразования типа?

Добавлено через 7 минут
Тогда так - можно ли с помощью Interlocked обеспечить потокобезопасность переменной типа перечисления?
К переменой применяется две операции - присвоение (изменение значения) и сравнение (в логике программы).

Добавлено через 9 минут
Нашел вот такое решение:
C#
1
2
3
4
5
6
private int _field;
public MyEnum Field  // added for convenience
{ 
  get { return (MyEnum)Interlocked.CompareExchange(ref _field, 0, 0); }
  set { Interlocked.Exchange(ref _field, (int)value); }
}
Это нормально? (пример старый 2011 года)

Добавлено через 11 минут
Поскольку защищено изменение переменной )типа перечисления) Field, то само сравнение Field в программе можно выполнять обычным способом? Тем более, что в классе Interlocked защищенных операций сравнения (с возвратом типа bool) нет.

Добавлено через 11 минут
Нашел ещё паллиатив как раз на мою тему.
Я использую перечисление для описания возможных состояний класса. Предлагают эти же состояния описать с помощью свойств readonly специального класса-состояния, вместо перечисления.
А я так вначале и делал ,
потом перешел на перечисления и тут вспомнил про потокобезопасность (состояние может менять асинхронно). И оказалось, что обеспечить потокобезопасность переменой состояния типа перечисления сложнее, чем типа пользовательского класса (для класса есть методы Interlocked с параметрами типа object). Если я это правильно понимаю
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,109
Записей в блоге: 2
30.10.2020, 16:40
Цитата Сообщение от titan4ik Посмотреть сообщение
Нашел вот такое решение:
А что оно делает по вашему мнению?
Мне, кажется, вы совершено неверно понимаете как работает Interlocked.

Для данного примера никакой большей потокозащищённости, чем обычная реализация свойства, это не даёт.
Все операции присваивания Int32 и так атомарны. В том числе и для возврата return.
А возвращаемое значение для Exchange вы всё равно не используете.

Весь этот код по функции и потокозащищённости равнозначен обычному автоствойству:
C#
1
public MyEnum Field {get; set;} // Абсолютно тоже самое
1
Заблокирован
30.10.2020, 16:47  [ТС]
Иными словами, автосвойство типа перечисление автоматически потокозащищено?
Имею ввиду под потокозащищеностью - атомарность (непрерываемость) операции изменения значения.

Добавлено через 2 минуты
Не, нужно подумать. Типы int ведь тоже защищают. Короче говоря, зависит от того, что как данная переменная используется в программе.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,109
Записей в блоге: 2
30.10.2020, 16:59
Цитата Сообщение от titan4ik Посмотреть сообщение
Иными словами, автосвойство типа перечисление автоматически потокозащищено?
Если оно атомарного типа.

Добавлено через 4 минуты
Цитата Сообщение от titan4ik Посмотреть сообщение
Не, нужно подумать. Типы int ведь тоже защищают. Короче говоря. зависит от того, что как данная переменная используется в программе.
Если с переменной несколько операций и надо локировать всю группу.
Если же операция ОДНА, то никакой защиты не нужно.
C#
1
2
3
4
5
6
7
8
9
int a = 20;
int b = 10;
int c;
lock(obj)
{
   с = a + b; // Две операции: сложение и присваивание. Защита нужна.
}
 
int d = c; // Одна операция. Защита не нужна.
Добавлено через 7 минут
Да, и то пример неудачный.
Даже для сложений максимум какая может быть ошибка, что после присвоения с значения a и b уже другие.
Но с точки зрения потокозащищённости - это валидная ситуация.
Не валидная, если меняются две переменные, а в операции будет участвовать у одной переменной новое значение, а у другой старое.
На простом сложении примера не получится.
C#
1
2
3
4
5
6
7
8
9
10
11
12
int a;
int b;
int c;
 
// Чё-то делаем
 
lock(obj)
{ // Вот для такой группы операций защита нужна.
   a = arg1;
   b = arg2;
   с = a + b; 
}
0
Заблокирован
30.10.2020, 18:43  [ТС]
В общем, нужно различать защиту конкретной операции с переменной и защиту алгоритма (куска кода, в котором возможны коллизии из-за асинхронности). Interlocked это про защиту операции.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16140 / 11264 / 2888
Регистрация: 21.04.2018
Сообщений: 33,109
Записей в блоге: 2
30.10.2020, 19:31
Цитата Сообщение от titan4ik Посмотреть сообщение
Interlocked это про защиту операции.
Да.

Допустим, Add объединяет в один атомарный блок
C#
1
2
3
4
{
    location1 += value;
    return location1;
}

CompareExchange:
C#
1
2
3
4
5
6
{
    var prev = location1;
    if (location1.Eguals(comparand))
       location1 = value;
   return prev;
}
Exchange:
C#
1
2
3
4
5
{
   var prev = location1;
   location1 = value;
   return prev;
}
Как видите, эти блоки совершенно никакой дополнительной потокобезопасности (для использования вместо автосвойства) не дают.

Добавлено через 6 минут
Цитата Сообщение от titan4ik Посмотреть сообщение
Interlocked это про защиту операции.
При этом многие операции не требуют защиты в принципе.
Нет никакого смысла для атомарных типов защищать get {return _field;} и set {_field = value;}.
Эти операции и так производятся одной командой процессора и никакие потоки вмещаться в это не могут.
Для неатомарных типов (структур с полями, long на x86 и др.) эти операции уже требуют защиты.

Добавлено через 58 секунд
Если ваше перечисление не long под сборку х86, то для него тоже нет необходимости в защите.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
30.10.2020, 19:31
Помогаю со студенческими работами здесь

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

Потокобезопасность ConcurrentDictionary vs Dictionary
Добрый день. Довольно наивный вопрос, но ответа я на него не нашел. Я знаю что ConcurrentDictionary это потокобезопасный Dictionary. ...

Потокобезопасность, найти ошибки в коде
Привет всем. Есть код обхода в ширину. Я хочу использовать пр этом несколько потоков, как я понимаю необходимо сделать очередь...

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

Надо вывести название переменной, тип данных переменной, значение переменной
у нас есть переменная int variable = 0; Надо вывести название переменной, тип данных переменной, значение переменной как это сделать?


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
SDL3 для Desktop (MinGW): Рисуем цветные прямоугольники с помощью рисовальщика SDL3 на Си и C++
8Observer8 17.03.2026
Содержание блога Финальные проекты на Си и на C++: finish-rectangles-sdl3-c. zip finish-rectangles-sdl3-cpp. zip
Символические и жёсткие ссылки в Linux.
algri14 15.03.2026
Существует два типа ссылок — символические и жёсткие. Ссылка в Linux — это запись в каталоге, которая может указывать либо на inode «файла-ИСТОЧНИКА», тогда это будет «жёсткая ссылка» (hard link),. . .
[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ФедосеевПавел 14.03.2026
Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора ВВЕДЕНИЕ Выполняя задание на управление насосной группой заполнения резервуара,. . .
делаю науч статью по влиянию грибов на сукцессию
anaschu 13.03.2026
прикрепляю статью
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru