makarychev13
@makarychev13
.NET-developer

При многопоточной работе исключена вероятность одновременнего доступа к ресурсам?

Заранее извиняюсь за большое количество текста. Я изучил довольно много информации на тему потоков. Разобрался с lock, мониторами, семафорами и мьютексами. Читал про SynchronizationAttribute, волатильность типов и класс Interlocked. Всё это мне более-менее понятно, но есть фундаментальный вопрос, который делает все остальные знания по теме бесполезными.

Как я понял, когда я запускаю несколько потоков, то они не работают одновременно. На деле процессор сначала n секунд занимается одним потоком, потом n секунд - другим и т. д. Это называется переключение контекста. Но тогда выходит, что одновременное обращение к переменной класса из разных потоков не возможно ? Ведь на деле мы просто сначала n секунд будем работать с переменной из одного потока, потом n секунд - из другого и т.д. По этой же логике выходит, что мы можем спокойно добавлять данные в список и удалять данные из того же списка из разных потоков? Но тогда не очень понятно, зачем нужны классы из неймспейса System.Collections.Concurrent.

Изучив информацию, я запомнил (но не понял), что атомарные операции безопасны при работе в многопоточном коде (это так?). Именно поэтому, например, инкрементация переменной с помощью Interlocked.Add безопасна и не приводит к проблемам. Текст получился несколько сумбурным, поэтому постараюсь подытожить:
1. Если никакой одновременности работы потоков нет, то как возможно одновременное изменение переменной из разных потоков?
2. Почему атомарные операции всегда безопасны в отношении потоков?
  • Вопрос задан
  • 339 просмотров
Решения вопроса 4
@Meloman19
Ты не совсем понял проблему многопоточности. Да, чтение неизменяемого значения из множества потоков безопасно, но вот изменение - нет.

На примере:
Лежит в памяти значение x = 1. Пока его только читают, всё ок, но предположим, что это у нас какой-то счётчик и два потока хотят его увеличивать на 1.

Первый поток считывает значение, кладёт его в стек, добавляет 1 и после записывает результат (2) обратно. Сам видишь, что тут отнюдь не одна операция, и между моментом считывания и записью проходит время. В это время второй поток может спокойно успеть считать пока ещё старое значение. Выходит, что оба потока возьмут 1, увеличать её на 1 и запишут в память, не важно в каком порядке. В результате вроде как оба потока отработало код, но значение в памяти увеличено только на 1. В этом и проблема.

Списки, к примеру, внутри хранят тот же счётчик для количества элементов, поэтому одновременная запись может неправильно отработать.
Ответ написан
petermzg
@petermzg
Самый лучший программист
Переключение контекста сохраняет все регистры процессора для потока и востанавливает для активного.
Если у вас идет обращение к переменной через регистр (см. volatile), а не напрямую к адресу памяти, то сохранность актуального значения не гарантируется. Примерный сценарий:
1. Первый поток поместил значение переменной в регистр (mov ax, [4C00h])
2. Второй поток сохранил его в регистр (mov ax, [4C00h])
3. Первый сделал какие-то операции над значением в регистре
4. Второй сделал.
5. Первый сохраняет значение по адресу памяти (mov [4C00h], ax)
6. Второй.

И вот какое значение сохранилось? Ведь каждый поток оперировал своими копиями регистров. И четкая последовательность вызова потоков тоже не гарантируется.
Ответ написан
Комментировать
Михаил имеется в виду что значение переменной может измениться при переходе от одной строки программы
x = 1;
к следующей строке
Console.Error.WriteLine(x=" + x.ToString()); // undefined


даже если никакого кода меняющего x
там нет - а произошло переключение конткста и другой поток записал свое значение в эту переменную
Ответ написан
ApeCoder
@ApeCoder
Как я понял, когда я запускаю несколько потоков, то они не работают одновременно. На деле процессор сначала n секунд занимается одним потоком, потом n секунд - другим и т. д. Это называется переключение контекста.


Процессоров и ядер может быть несколько. Соотвествтвенно эти рассуждения только в случае одного процессора и одного ядра. Но и в случае одного ядра атомарность операций не гарантированна см комментарий Meloman19

https://habr.com/ru/company/intel/blog/243385/
https://www.howtogeek.com/194756/cpu-basics-multip...
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы