Рубрика «Mutex»

Go: жарим общие данные. Атомно, быстро и без мьютексов - 1

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

Но в этой статье мы поговорим о другом. Современные процессоры имеют поддержку атомарных операций, что позволяет на основе них организовывать работу с общими данными до нескольких раз быстрее, чем с помощью общепринятых вариантов. Так как мьютексы реализованы на основе ОС, каналы сделаны на основе внутреннего кода Go с использованием тех же мьютексов из ОС под капотом, а атомарные операции делает сам процессор аппаратно за существенно меньшее количество тактов.Читать полностью »

Атомики в Go - это один из методов синхронизации горутин. Они находятся в пакете стандартной библиотеки sync/atomic. Некоторые статьи сравнивают atomics с mutex, так как это примитивы синхронизации низкого уровня. Они предоставляют бенчмарки и сравнения по скорости, например Go: How to Reduce Lock Contention with the Atomic Package.

Читать полностью »

Почему мьютексы в Rust реализованы именно так - 1

Я часто слышу от пробующих работать с Rust системных программистов жалобы на мьютексы и особенно на Rust Mutex API. Жалобы обычно выглядят так:

  • Они не хотят, чтобы мьютекс содержал данные, только блокировку.
  • Они не хотят управлять «защитным» значением, разблокирующим мьютекс при сбросе, в частности, они просто хотят вызывать операцию unlock, потому что им кажется, что это более явное действие.

Такие изменения превратили бы Rust mutex API в эквивалент C/Posix mutex API. Однажды я даже видел, как один разработчик пытался использовать Mutex<()> и разные хитрости, чтобы его имитировать.

Однако у такого стремления есть проблема: эти два аспекта Mutex неразрывно связаны друг с другом, а также с гарантиями безопасности Rust в целом — изменение одного из них или обоих откроет возможности для возникновения незаметных багов и повреждений из-за гонок данных.

Использование API мьютексов в стиле C, состоящего из набора косвенно защищаемых данных и из функций lock и unlock было бы опрометчивым в Rust, потому что это позволяет безопасному коду легко вносить ошибки, нарушающие безопасность памяти и вызывающие гонки данных.

Прозвучит спорно, но я утверждаю, что это справедливо и для C. Просто в Rust это более очевидно, поскольку Rust тщательно разделяет понятия «безопасного» кода, в который невозможно внести подобные ошибки, и «небезопасного» кода, в который можно вносить такие ошибки. В C такого разделения нет, и в результате этого использующий мьютексы код на C может тривиальным образом создавать серьёзные баги, которые потенциально можно подвергать эксплойтам.

В этом посте я разберу типичный C mutex API, сравню его с типичным Rust mutex API, и расскажу о том, что произойдёт, если мы изменим Rust API так, чтобы он напоминал C.
Читать полностью »

Корутины и синхронизация. Лучше не смешивать - 1

Предположим, у вас в коде есть критическая секция, которая не должна выполняться более, чем одним потоком одновременно.

В мире Java одним из стандартных решений является добавление ключевого слова synchronized к сигнатуре метода. В Kotlin для получения того же эффекта используется аннотация @Synchronized

Читать полностью »

Deadlock Empire — игра для разработчиков - 1

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

Встречайте: Deadlock Empire!

Суть игры — управление планировкой потоков, код которых приведен в заданиях. Управлять нужно так, чтобы добиться ошибочной ситуации: взаимблокировки, повторных значений счетчиков, достижения определенных участков кода и прочих непредвиденных ситуаций, которые нужно осознавать при разработке многопоточных приложений.
Читать полностью »

Одним из нововведений в Go 1.9 было добавление в стандартную библиотеку нового типа sync.Map, и если вы ещё не разобрались что это и для чего он нужен, то эта статья для вас.

Для тех, кому интересен только вывод, TL;DR:

если у вас высоконагруженная (и 100нс решают) система с большим количеством ядер процессора (32+), вы можете захотеть использовать sync.Map вместо стандартного map+sync.RWMutex. В остальных случаях, sync.Map особо не нужен.

Разбираемся с новым sync.Map в Go 1.9 - 1

Если же интересны подробности, то давайте начнем с основ.

Читать полностью »

Последняя статья про классические примитивы синхронизации.

(Наверное, потом напишу ещё одну про совсем уже нетипичную задачу, но это потом.)

Сегодня мы немножко заглянем в процессор. Чуть-чуть.

По сути, мы будем говорить про единственный примитив, который принципиально отличается от остальных: спинлок. Spinlock.

В комментариях к предыдущим заметкам возникла дискуссия — насколько справедливо вообще выделять спинлок как примитив, ведь по сути он — просто мьютекс, верно? Он выполняет ту же функцию — запрещает одновременное исполнение фрагмента кода несколькими параллельными нитями.

На уровне процесса всё так и есть — различия между спинлоком и мьютексом — чисто технические, вопрос реализации и производительности.

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

Дело в том, что внутри ядра мьютекс реализован с помощью спинлоков, а вот спинлоки реализованы сами по себе, автономно. Они — действительно базовый примитив. Ниже — только сам процессор.

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

Итак, иерархия реализации такова: mutex/cond/sema сделаны на базе спинлоков, спинлоки — на базе атомарных операций, предоставляемых процессором. Мы в них немного заглянем сегодня.

Как устроен спинлок?
Читать полностью »

Синхронизация нужна в любой малтитредной программе. (Если, конечно, она не состоит из локлесс алгоритмов на 100%, что вряд ли). Будь то приложение или компонента ядра современной операционной системы.

Меня всё нижесказанное, конечно, больше волнует с точки зрения разработки ядра ОС. Но почти всё применимо и к пользовательскому коду.

Кстати, ядра старых ОС в примитивах синхронизации не нуждались, поскольку преемптивной мультизадачности внутри ядра в старые добрые времена не было. (Уж за Юникс 7-й версии я отвечаю. Не было.) Точнее, единственным методом синхронизации был запрет прерываний. Но об этом позже.

Сначала перечислим героев. Мне известны следующие примитивы синхронизации:

User/kernel mode: mutex+cond, sema, enter/leave critical section.
Kernel only: spinlock, управление прерываниями.

Зачем всё это нужно, читатель, наверное, знает, но всё же уточним.

Если некоторая структура данных может быть доступна двум параллельно работающим нитям (или нити и прерыванию), и являет собой сущность, к которой нельзя обеспечить атомарный доступ, то работу с такой структурой нужно производить так, чтобы только одна нить одновременно выполняла сложные манипуляции состоянием структуры.
Читать полностью »

Здравствуй!

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

Так, недавно, я с интересом просматривал презентацию Степана Кольцова о языке программирования Rust, и мне очень понравилась идея реализации мьютаксов в этом языке. Причём никаких препятствий для реализации подобного примитива в C++ я не увидел и сразу же открыл IDE, с целью реализовать подобное на практике.
Читать полностью »

Решил однажды для себя я соорудить погодную станцию. Датчики там разные, в том числе на шине I2C. И как годится, обычно вначале, сделал все на флагах ожидания. Но путь настоящего джедая иной, и было решено все повесить на прерывания. Вот тут и начался геморрой. Проблема, с которой я столкнулся это обработка нескольких подряд идущих запросов. Например датчик давления BMP085 для дальнейшей работы с ним, требует вытянуть из его EEPROM 11 калибровочных констант:

Реализация мьютекса вне ОС на примере микроконтроллера AVR и шины TWI

О том как я пришел к решению и последовательность хода мыслей изложены ниже.
Читать полностью »


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js