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

Метод Interlocked.Exchange(Double, Double) не гарантирует одинаковое значение переменной в разных потоках?

20.03.2024, 13:45. Показов 2551. Ответов 48

Студворк — интернет-сервис помощи студентам
Метод Interlocked.Exchange(Double, Double) обеспечивает атомарность операции присвоения нового значения переменной типа Double.
То есть, InterlockedExchange гарантирует, что изменение переменной и возврат ее исходного значения не будут прерваны другими потоками.
Но есть ли гарантия, что данная переменная будет после этого иметь одно и то же значение в разных потоках, в том числе, исполняемых на разных процессорах? Похоже, что нет.
Но если нет, то тогда для обеспечения одинакового значения переменной нужно применять lock?
И тогда, если блокировать код присвоения с помощью lock, то тогда Interlocked.Exchange уже нет смысла использовать?
Правилен ли вывод, что Interlocked.Exchange имеет смысл применять в том случае, когда не нужно, чтобы значение переменной во всех потоках было одинаковым (самым последним)?
А преимущество Interlocked.Exchange по сравнению с блокировкой lock в том, что Interlocked.Exchange не тормозит потоки?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
20.03.2024, 13:45
Ответы с готовыми решениями:

[Error] cannot convert 'double (*)(double)' to 'double' for argument '1' to 'double pow(double, double)'
#include <iostream> #include <math.h> using namespace std; int main () { system("cls"); double x,y; system ("echo...

Ошибки error C2296: -: недопустимо, левый операнд имеет тип "double (__cdecl *)(double,double,double
Думаю из-за polp #include<iostream> #include<cmath> #include<cstdlib> using namespace std; double polp(double af,double...

Написать функцию int Search ( double A[], int n , double x), которая находит в массиве double A[n] элемент, значение которого равно x
Написать функцию int Search ( double A, int n , double x), которая находит в массиве double A элемент, значение которого равно x. Функция...

48
1152 / 860 / 263
Регистрация: 30.04.2009
Сообщений: 3,603
27.03.2024, 21:31
Студворк — интернет-сервис помощи студентам
titan4ik, Interlocked для атомарных операций, которые иначе выполнялись бы как последовательность, а так то да, я бы использовал Volatile.Read
1
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,226
27.03.2024, 23:21
Цитата Сообщение от titan4ik Посмотреть сообщение
№2 - Минусы - это как из пушки по воробьям.
Не надо мешать блокирующие примитивы синхронизации и инструменты языка для атомарных операций.

Цитата Сообщение от titan4ik Посмотреть сообщение
№3 - минусов нет
А барьеры памяти магическим образом исчезают чтоль? titan4ik, вы читали тут старую статью выше от Игоря Островского? К посту от nicolas2008 есть много вопросов.
0
Заблокирован
28.03.2024, 00:08  [ТС]
IamRain,
Лучше бы Вы ссылались на сообщение от nicolas2008, я ведь просто скопировал его утверждения. Сам я ничего не утверждаю.
Цитата Сообщение от IamRain Посмотреть сообщение
К посту от nicolas2008 есть много вопросов.
Это лучше с nicolas2008, обсудить. А мне было бы полезно посмотреть это обсуждение.
0
1152 / 860 / 263
Регистрация: 30.04.2009
Сообщений: 3,603
28.03.2024, 03:13
Цитата Сообщение от IamRain Посмотреть сообщение
Не надо мешать блокирующие примитивы синхронизации и инструменты языка для атомарных операций.
А кто мешает?
Метод указан для общего развития. При вызове Monitor.Enter неявно создается MemoryBarrier.

Цитата Сообщение от IamRain Посмотреть сообщение
А барьеры памяти магическим образом исчезают чтоль?
Барьер памяти как раз создается, как и в остальных трех методах.

Цитата Сообщение от IamRain Посмотреть сообщение
К посту от nicolas2008 есть много вопросов.
К вашему комментарию много вопросов. В чем проблема с методом 3.
0
Заблокирован
28.03.2024, 09:06  [ТС]
Цитата Сообщение от nicolas2008 Посмотреть сообщение
Барьер памяти как раз создается, как и в остальных трех методах.
Я пытаюсь практическое зерно сформулирвать:
Если во всех трех этих (№№ 1, 3 и 4) методах создается идентичный барьер памяти, то по этой своей сути они равнозначны.
И у них общий минус общий - "все попытки чтения будут включать вызов Thread.MemoryBarrier(), а это доп издержки которые не всегда нужны."
А отличия лежат в другой плоскости. Насколько они значимы эти отличия?
Проблемы психологического характера ("может ввести в ступор большинство погромистов, повышен риск что непонятный код будет удален после короткого тестирования и подтверждения что "все и так работает нормально") понятны и меня мало волнуют.
А у метода №2 с lock - самые большие издержки.
То есть, пока, кажется так, что можно любой из методов №№ 1, 3 и 4 применять с равным успехом. И что все они лучше по издержкам метода №2 примерно в равной степени.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16116 / 11237 / 2887
Регистрация: 21.04.2018
Сообщений: 33,038
Записей в блоге: 2
29.03.2024, 00:02
titan4ik, есть ещё такой способ чтения:

C#
1
Interlocked.CompareExchange(ref someVariable, 0.0, 0.0)
1
Заблокирован
29.03.2024, 00:18  [ТС]
Как раз сегодня увидел плакат с такой надписью:
.NET разработчики: чего мы хотим? Меньше альтернатив!
2
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,226
29.03.2024, 00:43
Цитата Сообщение от nicolas2008 Посмотреть сообщение
А кто мешает?
Имелось ввиду, что оригинальный вопрос был про Interlocked класс и все, что с ним связано - атомарные операции и запрет на reordering optimization (то, что умеет volatile).
Уточняю: зачем здесь в списке сравнения lock? Почему бы сюда коней не добавить - не рассказать про Mutex-ы и Semaphore-ы еще?
0
Заблокирован
29.03.2024, 00:50  [ТС]
IamRain, мешанина если и есть, то у меня в голове А это нашло отражение и в тексте вопроса (там и lock фигурирует). Так что lock помянуть в ответах было уместно и правильно.

Добавлено через 1 минуту
Правильный вопрос должен был бы звучать так - Как сделать так, чтобы всё было просто, хорошо и за это ничего не было)
0
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,226
29.03.2024, 00:53
Цитата Сообщение от titan4ik Посмотреть сообщение
Если во всех трех этих (№№ 1, 3 и 4) методах создается идентичный барьер памяти, то по этой своей сути они равнозначны.
volatile - это запрет на reordering, работающий при чтении и при записи по-разному, насколько я понимаю.
А Volatile.Read - это, очевидно, только на чтение. То есть через класс Volatile можно более гибко работать с меньшим оверхедом по перфомансу.
Ну это все очень нишевое применение - тот, кому понадобится, сам найдет наиболее подходящий способ.

Добавлено через 1 минуту
Цитата Сообщение от titan4ik Посмотреть сообщение
Так что lock помянуть в ответах было уместно и правильно.
Ну ок.
1
1152 / 860 / 263
Регистрация: 30.04.2009
Сообщений: 3,603
29.03.2024, 03:02
Цитата Сообщение от IamRain Посмотреть сообщение
Почему бы сюда коней не добавить - не рассказать про Mutex-ы и Semaphore-ы еще?
Можно рассказать почему нет Но это все из разряда kernel lock, по сути тот же метод 2
0
Заблокирован
29.03.2024, 10:09  [ТС]
1) Посмотрел сейчас описание класса Volatile на русском языке. Трудно понять. Видимо машинный перевод.
Но главное понял - если в одном потоке произошла запись в переменную с помощью метода Volatile.Write(), то это не гарантирует, что при чтении в другом потоке с помощью метода Volatile.Read(), даже в последующем по времени (!) относительно Volatile.Write(), будет прочитано уже измененное значение переменной. Не гарантирует.
То есть, методы чтения и записи класса Volatile гарантируют только отсутствие перестановки операций.
Если Volatile.Write(), то операции, которые идут до него, не могу быть перемещены после него.
Если Volatile.Read(), то операции, которые идут после него, не могу быть перемещены до него.
2) Если используется модификатор переменной volatile, то он запрещает перемещать операции через операцию с этой переменной в обе стороны - предшествующие и последующие операции такими и останутся. Но volatile тоже не гарантирует, что во втором потоке будет прочитано значение уже измененное в первом.
3) А если нужны гарантии того, чтобы во всех потоках переменная имела одинаковое значение, то нужно либо очень изощрённо напрягать мосх, либо обходиться методами Interlocked, либо использовать блокировку lock, либо что-то вроде потокобезопасных коллекций.
Нужно ещё и это посмотреть;
упрощенные механизмы синхронизации, например SpinLock, SpinWait, SemaphoreSlim и CountdownEvent, которые впервые введены в .NET Framework 4. Эти типы синхронизации обычно используют цикличную работу для коротких промежутков перед помещением потока в фактическое состояние ожидания.
P.S.
Интересно, потокобезопасные коллекции есть, а потокобезопасных переменных хотя бы базовых классов (int, double и т.п.) нет.
0
1152 / 860 / 263
Регистрация: 30.04.2009
Сообщений: 3,603
30.03.2024, 22:15
titan4ik, а где это такое написано что "не гарантирует"?
Вся потокобезопасность построена на блокировках, атомарных операциях и барьерах памяти. Никаких магических средств в потокобезопасных коллекциях нет, все строится на тех же примитивах.

Добавлено через 2 минуты
Можете еще про когерентность кеша процессора почитать (согласованность данных для разных ядер). В интернетах пишут что в современных процессорах кеш общий и/или всегда согласован.
1
Заблокирован
30.03.2024, 22:32  [ТС]
Цитата Сообщение от nicolas2008 Посмотреть сообщение
В интернетах пишут что в современных процессорах кеш общий и/или всегда согласован.
В итоге получается, что работа многопоточной программы существенно зависит от реализации "вычислительной системы". Надеюсь, что у меня есть (или будет) современный процессор
Цитата Сообщение от nicolas2008 Посмотреть сообщение
а где это такое написано что "не гарантирует"?
Even though the volatile write to y on thread 1 occurred before the volatile read of y on thread 2, thread 2 may still see y2 == 0. The volatile write to y does not guarantee that a following volatile read of y on a different processor will see the updated value.
https://learn.microsoft.com/en... ew=net-8.0
Возможно, я не так понял.
Там есть ещё примечание.
Volatile reads and writes ensure that a value is read or written to memory and not cached (for example, in a processor register). Thus, you can use these operations to synchronize access to a field that can be updated by another thread or by hardware.
Но по сути своей "примечание" должно дополнять текст, а не исправлять его.
Возможно, я не так понял. Буду рад, если это так. Тогда проще жить!
1
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16116 / 11237 / 2887
Регистрация: 21.04.2018
Сообщений: 33,038
Записей в блоге: 2
30.03.2024, 23:09
Цитата Сообщение от titan4ik Посмотреть сообщение
Но главное понял - если в одном потоке произошла запись в переменную с помощью метода Volatile.Write(), то это не гарантирует, что при чтении в другом потоке с помощью метода Volatile.Read(),
Это такая эфемерная характеристика "время" для мультипотока, да ещё на разных процессорах (ядрах).

Очень приблизительно:
1) Есть некий флаг в Москве и его дубли в регионах;
2) Когда в регионе флаг подымается/опускается - посылается письмо в Москву произвести там тоже действие;
3) Когда надо в регионе определить состояние флага - то посылается письмо в Москву и там отвечают.

Добавлено через 4 минуты
Решили поднять Флаг на Камчатке и послали письмо в Москву. Письмо идёт в Москву неделю.
А через два дня в Рязани решили проверить состояние флага. Письмо идёт в Москву за день. И там отвечают что флаг опущен, хотя на Камчатке его уже подняли и они считаю, что по всей стране уже подняли.

Добавлено через 9 минут
Локирование же это такой процесс:
1) Прежде чем узнать/изменить состояние флага - посылается письмо-запрос в Москву;
2) В Москве очередь писем с запросами. Берут первое из очереди и по нему отвечают "Начинайте действовать". Все остальные запросы отстаются ждать в очереди;
3) В регионе получили разрешение. После этого посылают в Москву письмо сообщить или изменить состояние;
4) Когда для региона станет неважен Флаг, посылают письмо об этом;
5) Когда получили от региона письмо о завершении работы с флагом - берут следующий запрос из очереди.

В этой схеме гарантированно после изменения состояния флага, по всем последующим запросам будет правильный ответ. Но, во-первых,это многократно затратнее по времени. Если учитывать время ожидания в очереди, то в десятки-сотни-тысячи раз. Во-вторых, здесь вместо гонки изменить-узнать, получаем гонку запросов. Запрос отправленный позже из ближайшего региона попадёт в очередь раньше чем из дальнего.
1
Заблокирован
31.03.2024, 09:38  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Это такая эфемерная характеристика "время" для мультипотока, да ещё на разных процессорах (ядрах).
Но это плохо. Потому и есть необходимость синхронизации данных путём специальных программных уловок.
Собственно, в материалах по параллельному программированию всегда по 10 раз пишут одно и то же - старайтесь, чтобы обрабатываемые параллельные процессы не использовали общих данных. Чтобы изначально уйти от источника проблем.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16116 / 11237 / 2887
Регистрация: 21.04.2018
Сообщений: 33,038
Записей в блоге: 2
31.03.2024, 10:25
Цитата Сообщение от titan4ik Посмотреть сообщение
Потому и есть необходимость синхронизации данных путём специальных программных уловок.
Совершенно верно.
Всё зависит от условий конкретной задачи.
Так как, то что подразумевается под синхронизацией зависит от конкретного задания.
Точное совпадение по таймингам в разных потоках - это исключительно редкое условие. Оно больше свойственно каким-то реалтайм системам, чем обычной обычному DeskTop под Виндой.
Под Виндой для мультипотока почти всегда требуется обеспечение потокобезопасности при работе с данными.
Условно: считал значение X, произвёл какие-то вычисления, записал новое значение X. И здесь требуется гарантировать, что значение X не будет изменяться из другого потока на время этих действий.
Или считываешь коллекцию (в том числе байты Double) и нужно гарантировать, что в процессе считывания не никто не меняет эту коллекцию. Чтобы не получить, например для Double, первые байты от одного значения, а последние от другого.

Точная синхронизация по времени, если нужно, осуществима, но это всегда "дорого".
0
Заблокирован
31.03.2024, 11:12  [ТС]
Цитата Сообщение от nicolas2008 Посмотреть сообщение
а где это такое написано что "не гарантирует"?
nicolas2008, я так и не понял, каждый раз, когда я пытаюсь кратко резюмировать ответ на главный вопрос - каким способом можно обеспечить потокобезопасность данных, у кого-то появляется ремарка, мол, что-то не так я формулирую.
Моя последняя формулировка тоже не верна? Я потом привел ссылки.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16116 / 11237 / 2887
Регистрация: 21.04.2018
Сообщений: 33,038
Записей в блоге: 2
31.03.2024, 12:04
Цитата Сообщение от titan4ik Посмотреть сообщение
Моя последняя формулировка тоже не верна? Я потом привел ссылки.
Последовательно.
Цитата Сообщение от titan4ik Посмотреть сообщение
Но главное понял - если в одном потоке произошла запись в переменную с помощью метода Volatile.Write(), то это не гарантирует, что при чтении в другом потоке с помощью метода Volatile.Read(), даже в последующем по времени (!) относительно Volatile.Write(), будет прочитано уже измененное значение переменной. Не гарантирует.
И да, и нет.
Главный вопрос "что считать последующем по времени"?
Если приход сообщений в какой-то узел (как я описывал выше в Москву) - то гарантирует.
Если отправку сообщений - то нет.

Volatile - это всего лишь отправка сообщений "в центр". Функции синхронизации времени между потоками его методы не имеют.

Цитата Сообщение от titan4ik Посмотреть сообщение
2) Если используется модификатор переменной volatile, то он запрещает перемещать операции через операцию с этой переменной в обе стороны - предшествующие и последующие операции такими и останутся. Но volatile тоже не гарантирует, что во втором потоке будет прочитано значение уже измененное в первом.
Да.


Цитата Сообщение от titan4ik Посмотреть сообщение
3) А если нужны гарантии того, чтобы во всех потоках переменная имела одинаковое значение, то нужно либо очень изощрённо напрягать мосх, либо обходиться методами Interlocked, либо использовать блокировку lock, либо что-то вроде потокобезопасных коллекций.
И да, и нет.

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

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

Мютекс, lock и т.п. - это уже действительно про синхронизацию.
Но все они дорогие.
1
Заблокирован
31.03.2024, 13:11  [ТС]
Да, всё не просто.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Volatile - это всего лишь отправка сообщений "в центр". Функции синхронизации времени между потоками его методы не имеют.
Если Volatile обеспечивает взаимодействие потоков через некий "центр", то это уже хорошо. В этом случае, если в "центре" изменилось значение переменной, то при чтении из любого потока оно должно быть прочитано правильно. Хотя ваш пример с флагами показывает, что не всегда так. Да. Понятно. В общем нужно знать как это всё работает на низком уровне. А на низком уровне всё определяется системой и ещё ниже особенностями устройства процессора.
Тут прямо пишут, что синхронизации нет:
Несмотря на то, что переменная запись в потоке 1 произошла до переменного чтения y в потоке 2, поток 2 по-прежнему y может видеть y2 == 0. Переменная запись в y не гарантирует, что при следующем переменном чтении y на другом процессоре будет отображаться обновленное значение.
https://learn.microsoft.com/ru... .0#remarks
Но ниже в примечании такой текст:
Переменные операции чтения и записи гарантируют, что значение считывается или записывается в память, а не кэшируется (например, в регистре процессора). Таким образом, эти операции можно использовать для синхронизации доступа к полю, которое может быть обновлено другим потоком или оборудованием.
??? То есть, флаг по сути один - в центре. А из регионов (потоков) можно только посмотреть на флаг или дать команду на его смену.
??? и что в итоге???
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Interlocked - это про атомарность, а не про синхронизацию времени между потоками. То есть чтобы пока считываешь первый байт Double, другой поток не изменил последний байт Double.
Насколько я понимаю, interlockid может обеспечивать потокобезопасность.
Цитата Сообщение от IamRain Посмотреть сообщение
Если есть требование к реализации потокобезопасного lock-free кода, то одним из вариантов может быть как раз Interlocked.Exchange. В официальных доках по методу есть наглядный пример.
https://learn.microsoft.com/en... stem-int32
(и даже ссылка у них работает не правильно! - попадаем на начало статьи )

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

Добавлено через 2 минуты
А так. Слишком много "Но". И слишком много противоречий в текстах и мнениях.

Добавлено через 10 минут
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
31.03.2024, 13:11
Помогаю со студенческими работами здесь

Ошибка: error LNK2001: unresolved external symbol "double __cdecl Akk(double,double,double)"
#include <iostream> #include <cmath> using namespace std; double Akk(double x, double y, double z); int main() { int a, b, c; ...

Отметьте 3 ошибочных объявления переменной типа double, Double
Добрый день! Пожалуйста, помогите разобраться. Отметьте 3 ошибочных объявления переменной типа double, Double 1)double d1 = 0.0f;...

Отметьте 3 ошибочных объявления переменной типа double, Double
Добрый день! Пожалуйста, помогите разобраться. Отметьте 3 ошибочных объявления переменной типа double, Double 1)double d1 = 0.0f;...

Перегрузите метод f так, чтобы соответствовала виду static void f (double x, out double y)
ПОМОГИТЕ!! Выдаёт ошибку! До передачи управления из текущего метода выходному параметру "y" должно быть присвоено значение. ...

Перегрузите метод f так, чтобы его сигнатура соответствовала виду static void f (double x, out double y)
Как сделать метод, чтобы он соответствовал заданию? class Program { static double f(double x) { ...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru