Pull to refresh

Comments 549

PinnedPinned comments

А-а-а, этот момент. Понял.

я там дальше ниже читал-читал, читал-читал, но ваша войнушка так далеко зашла, что плюнул и вышел:)

Добро. Желаю удачи все нам уже разобраться один раз с этим дерьмом.

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

Все синьоры заняты в суровом проде, и некому разрабатывать стандарты.

Две школы засели по своим лагерям, и строят костыли системам придуманным до 90х и соазу после.

Железняки нам подари многопоток, и кодеры вместо этого нет придумать новую парадигму, но все заняты костылями. И Web, конечно тут постарался со своими палками)

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

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

При реализации собственных пет-проектов на C++ / WTL, использование многопоточности не привело, лично у меня, к каким-то особым проблемам в использовании ООП (см. скриншот моей неопубликованной программы «МедиаТекст»: http://scholium.webservis.ru/Pics/MediaText.png ). Да, менеджер событий можно было бы оптимизировать, но менеджер потоков там работал нормально.

Другой мой проект (см. мою статью: «Новая компьютерная программа для запоминания иностранных слов и фраз» в https://habr.com/ru/articles/848836/ ) использовал, в том числе, эмуляцию видео-режима. Не знаю, можно ли считать работу с таймером многопоточностью либо его эмуляцией, но проблемы там были не в ООП. Наоборот, решение своих проблем я связывал с «правильным» использованием ООП.

наследование, инкапсуляция, классы

Как бы там ни было, но для пет-проектов это вполне работает.

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

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

Естественно! Иначе, говорил бы о профессиональных проектах. Но, к сожалению, я только любитель…

потерял две минуты жизни читая эту ветку

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

Два вызова метода increment из двух разных потоков приведет к тому, что мы дважды вычитаем предыдущее значение из базы, а потом дважды запишем обратно его же, увеличенное на единицу.

Оказывается, у вас проблемы не только с мьютексами, но и с БД

Да я вообще тупой, что уж там.

Так не нужно корявым примером работы с БД аргументировать наличие проблем в ООП. Или используйте атомарный инкремент средствами самой БД или меняйте алгоритм работы класса на многопоточный.

Ведь ни мьютексы, ни ООП тут не причем, так как эти инструменты действительно нужно уметь правильно готовить использовать.

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

Знаете, вы меня просто вынуждаете согласиться с вашим предыдущим комментарием :-)

атомарный инкремент средствами самой БД

ООП как бы обещает защиту данных через инкапсуляцию и абстракцию, а у вас абстракция протекает

Вы можете развернуть свой комментарий? Если честно, то я его не понял.

Чтобы инкапсуляция работала, как задумано, — надо правильно написать код внутри.

фальшивые дублоны принимаете?

по быстрому могу только через LLM

Обещание ООП

ООП говорит: "Вот объект BankAccount. У него есть приватное поле _balance. Только его методы, например Deposit(), могут менять это поле. Я, объект, гарантирую целостность своих данных". Это и есть инкапсуляция. А абстракция в ООП говорит, что мы не должны заботиться о реализации - БД это или скажем in-memory key-value словарь.

Суровая реальность (где протекает абстракция)

На самом деле, "истинное" значение баланса лежит не в памяти моего C# объекта, а в строке таблицы Accounts в PostgreSQL. Мой объект — это всего лишь временный, кешированный представитель этих данных.

И тут возникает конфликт:

  1. Наивный ООП-подход (протекающая абстракция):

    • Мой метод Deposit(amount) делает: this._balance += amount;

    • Затем другой слой (Repository/ORM) смотрит на измененный объект и генерирует SQL: UPDATE Accounts SET Balance = [новое значение] WHERE Id = ?

    • Проблема (Race Condition): Если два потока одновременно загрузят счёта с балансом 1000, оба в памяти увеличат его до 1100, и последний записавший затрет результат первого. Баланс будет 1100, а не 1200. Инкапсуляция объекта в C# никак не защитила данные в БД. Абстракция "безопасного объекта" протекла и оказалась ложью.

  2. Правильный подход (признание, что ты не главный) - протекающая абстракция:

    • Как вы и сказали, нужно использовать UPDATE Accounts SET Balance = Balance + 100 WHERE Id = ?.

    • Эту операцию БД выполнит атомарно. Она сама защитит свои данные.

    • Что это значит для моего ООП-объекта? Его метод Deposit() больше не должен менять свое состояние this._balance. Он должен сгенерировать команду (UPDATE...) и отправить ее в базу данных.

Итог:

Когда появляется внешний авторитетный источник состояния (как БД), классическая ООП-инкапсуляция и абстрагирование от реализации в БД ломается. Объект больше не является хозяином своих данных. Он превращается из хранителя состояния в генератор команд (Command Emitter) или фасад для внешних операций.

Попытка притвориться, что объект в памяти — это и есть "настоящий" объект, и есть та самая "протекающая абстракция". Правильное проектирование (например, в CQRS) признает этот факт и явно разделяет модели для чтения и модели для изменения, которые только принимают команды и валидируют их, а всю работу по атомарному изменению делегируют БД.

Фу. Это мой пример, только мысью по древу.

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

ну какой смог, я просто расписал как обещанная абстракция протекает
просто в терминах statemachine не все мыслят

Вы реально думаете, что LLM сумел уловить вашу мысль и у него получилось написать понятный ответ?

я понял только что вы не поняли

У вас странное понятие о протекающих абстракциях.

ООП говорит: "Вот объект BankAccount. У него есть приватное поле _balance. Только его методы, например Deposit(), могут менять это поле. Я, объект, гарантирую целостность своих данных". Это и есть инкапсуляция.

Это не инкапсуляция.

"Я объект BankAccount, представляющий банковский счет. У меня есть методы Deposit и Withdraw, позволяющие менять баланс счета. Я гарантирую целостность своих данных. Как я это делаю и в каком виде храню баланс - не твое собачье дело" - вот это инкапсуляция.

На самом деле, "истинное" значение баланса лежит не в памяти моего C# объекта, а в строке таблицы Accounts в PostgreSQL

Вообще по барабану, где оно лежит и как реализовано до тех пор, пока подробности не торчат наружу.

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

Вот у тебя вышеуказанный BankAccount. Допустим, ты пишешь реализацию: методы Deposit и Withdraw набирают номер телефона оператора и заставляют нейросеть зачитать голосом "увеличить баланс счета №192839102931012 на 100 рублей". Реализация говно, конечно, но абстракция - идеальна.

Почему идеальна абстракция? Ну, например, ты достаточно быстро понял, что дозвон может занимать до 5 минут, что сильно тормозит поток транзакций. Надо что-то делать. Например, батчевать. Пока номер набирается, копить команды и вываливать на оператора разом то, что накопилось: "счет 1923891243 +100, счет 932181292348 -100". Прелесть в том, что весь твой код, который подсчитывает, на сколько менять баланс, вообще ни в одной строчке не изменился. Все изменения - внутри класса BankAccount.

Окей, на 18-й итерации до тебя дошло, что с оператором что-то не так: то опечатывается, то трубку не берет, то рыдает и в запой уходит. Ты ставишь какую-нибудь постгрю и выкидываешь к чертовой матери всю эту дичь с телефонией. Заменяешь тупым запросом к БД. Изменения - монументальные, а абстракция не изменилась. Чтобы поменять баланс, тебе нужно дергать все те же ручки с теми же параметрами.

Вот это - абстракция, и она не течет. Хочешь пример текущей абстракции - посмотри на слайсы в Go. А вот то, что ты напридумывал, не течет.

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

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

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

Вот это я вам и пытаюсь донести.

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

Каким боком она подтекать-то начинает???

Вот у нас метод BankAccount.Deposit(decimal amount) - это наша абстракция.

public void Deposit(decimal amount) {
  this.amount += amount
}

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

Вот, смотрите, ща я реализацию исправлю, а абстракция та же самая останется:

public void Deposit(decimal amount) {
  this.mutex.Lock()
  this.amount += amount
  this.mutex.Unlock()
}

А абстракция та же, ничего никуда не утекло!

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

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

Два вызова метода increment из двух разных потоков приведет к тому, что мы дважды вычитаем предыдущее значение из базы, а потом дважды запишем обратно его же, увеличенное на единицу. Классический off-by-one в условиях высококонкурентной среды.

А что касается этого. Так сохраняйте события в бд (операция, значение, метка времени) + сжатие (например, на основе метки времени). Тогда никаких вычитываний для модификации не потребуется. Добавьте однопоточный механизм сжатия секвенции событий на основе метки времени, если не нужен детальный аудит изменений, либо используйте сжатие для аггрегации, чтобы быстро получать актуальное значение на определенный момент времени. То что у вас конкурентность вылезла - лишь проблема вашей реализации и архитектуры.

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

В многопотоке абстракция течёт. Потому что появляется время, которое разделяет состояние на до изменения и после. Но если объект сам не управляет временем, то до и после перемешивается.

Приведите пример в многопотоке.

Lock управляет временем (заставляет ждать), но способ не безопасный и не эффективный

В многопотоке абстракция течёт. Потому что появляется время, которое разделяет состояние на до изменения и после.

Ну, т.е. любая абстракция течет? о_О Всегда есть время до и после!

Приведите пример в многопотоке.

Приводил уже.

public void Deposit(decimal amount) {
  this.mutex.Lock()
  this.amount += amount
  this.mutex.Unlock()
}

Lock управляет временем (заставляет ждать)

Все верно, в этом и есть смысл абстракции! Абстракция - это класс BankAccount, который позволяет просто дернуть ручку Deposit, на задумываясь, что внутри. Мьютекс - внутри, в реализации. Нам не надо заботиться о необходимости лочить мьютекс снаружи, потому что внутри мы этим уже озаботились - это и называется "абстракция".

но способ не безопасный и не эффективный

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

  Всегда есть время до и после!

Нет, конечно

Вы можете развернуть свой комментарий? Если честно, то я его не понял

Могу и развернуть:

Учите, что такое инкапсуляция и абстракция. А потом задавайте вопросы.

Учите, что такое инкапсуляция и абстракция. А потом задавайте вопросы.

Вам самим это не мешает хорошенько выучить, так как LLM за вас думать не будет.

А с точки зрения ООП, речь всегда идет о внутренних данных объекта, тогда как в примере в статье используются внешние по отношению к объекту данные из БД.

с точки зрения ООП, речь всегда идет о внутренних данных объекта, тогда как в примере в статье используются внешние по отношению к объекту данные из БД

public int increment() {
  int current = this.value;
  // управление переключилось
  this.value = ++current;
  return this.value;
}

Завидую, когда у людей абстрактное мышление полностью вытеснено прикладным. Так понятнее?

Конечно! Ведь в этом примере можно вызывать increment() хоть в миллиарде потоках без каких либо гонок!

Вы сейчас серьёзно? Вы правда думаете, что гонки тут нет?

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

Это какой же компилятор такими чудесами промышляет?

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

Так и пишите: если класс напрямую объявлен, как synchronized, то […]. Так и джава умеет, вот только это кувалда, которая решает одну незатейливую проблему, а приносит — миллиард очень затейливых, наподобие неожиданных дедлоков в самых непредсказуемых местах.

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

А не синхронизированный класс компилятор не даст вам расшарить между потоками.

Чаво? [Здесь и далее я предполагаю, что под «не синхронизированный класс» имелось в виду «экземпляр класса, объявленного без ключевого слова synchronized».]

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

Понятнее не стало, а тратить время на подробное разбирательство с D у меня желания нет.

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

Причём тут язык D? Мы более абстрактно, не зависимо от языка обсуждаем вроде

Или по вашему ООП только там? Java не ООП?

Java не даёт безопасную многопоточность. При чём тут она?

Значит, ООП сам по себе не даёт гарантий многопоточности. И инструментов удобных в общем случае нет. О чём и спич

Гарантии даёт система типов, которая тоже ортогональна ООП.

Гарантии безопасного параллелизма даёт система типов?

Глаза же вытекают, не надо так.

Конечно дает!

Например, STM ;)

Если вы вызываете метод increment() у одного объекта из нескольких потоков (когда объект глобальный), тогда естественно гонка будет. Если объект локальный для потока (сервис, асинхронщина и т.д.), то гонки нет.

Тогда как в вашем изначальном примере с внешними данными из БД гонка есть всегда и не зависимо от локальности объекта.

в этом примере можно вызывать increment() хоть в миллиарде потоках без каких либо гонок!

Если вы вызываете метод increment() у одного объекта из нескольких потоков (когда объект глобальный), тогда естественно гонка будет. 

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

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

это может быть любой объект.

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

И для каждого варианта стратегия синхронизации доступа к методам объекта будет разной. Точнее, синхронизировать доступ к такому объекту нужно только тогда, когда он глобальный. Для локальных объектов синхронизация не требуется, а вот thread_safe зависит от условий его использования.

Короче, срочно в школу учить матчасть!

Для локальных объектов синхронизация не требуется […]

Это еще почему? Я не имею права внутри функции объявить переменную, присвоить и передать в два разных потока? Компилятор запретит?

Можно и адрес локальной переменой возвращать в return, можно даже писать по 0x0 адресу, никто вам этого не запретит и даже в Rust можно писать unsafe код.

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

Но для С++ это будет только соглашением, т.к. компилятор это не может проверить.

мы ведем разговор про синхронизацию доступа к объекту и в контексте нашей переписки «локальный» объект подразумевает отсутствие доступа к нему снаружи локальной области видимости

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

Если нет доступа — бессмысленно рассматривать гипотетическую ситуацию «вы вызываете метод increment() у одного объекта из нескольких потоков […]» (это прямая цитата из вашего комментария, если что). Если доступ есть — то есть и гонка (пока нет явного мьютекса, или его неявного аналога, известного в джаве под именем «synchronize»).

Всё вот именно так просто, тут нечего обсуждать.

Ну раз нечего обсуждать, тогда удачи вам хорошо пообщаться с LLM в другой ветке комментариев.

Видимо, под глобальностью вы имеете в виду обратное от локальности в одной функции. То есть объект могут иметь с двух сторон

То есть shared state.

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

В первом утверждении речь идёт об одном конкретном примере, во втором – о другом. Вы не вычеркнули, а вырвали из контекста. Если вычеркнутое вернуть, кажущееся противоречие исчезает.

Понятно, вам уже столько в панамку напихали, что только огрызаться и можете.

Простите великодушно, в каком смысле «напихали»? Три с половиной типичных хабрахомяка не поняли текст? — Тоже мне новость.

огрызаться и можете

Огрызаться? — Помилуйте. Просто вы сказали такую несусветную херню, что я даже сострить не нашелся.

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

Хотя вы вряд ли способны это осознать, защитное отрицание не даст.

вы вряд ли способны это осознать

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

Так что опять мимо.

А, вы даже не скрываете. Действительно, надо было сначала прочитать профиль.

с точки зрения ООП, речь всегда идет о внутренних данных объекта, тогда как в примере в статье используются внешние по отношению к объекту данные из БД

Так и есть. Но ООП заявляет (через инкапсуляцию и абстракцию) что оно может решить этот вопрос без погружения программиста в кишки СУБД. И ведь многие верят!

Это в каком месте ООП такое заявляет? Может, вы просто неверно поняли то, какие гарантии ООП дает?

Ну так и статья о том, что ооп не дает какие-то гарантии.

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

Я не являюсь практиющим программистом, но с таким наслаждением смотрю все эти святые войны))

Уже и в абстракцию с инкапсуляцией — понимать стал))

Я доволен 😊 Весьма интересно…

Эта ситуация: самолёт ждёт указаний диспетчера, но диспетчер не сможет дать никаких указаний, пока самолёт не сядет, — и есть типичный дедлок.

Кстати, насчет мьютексов и дидлоков. Эта проблема не мьютексов, как объектов, а их архитектуры (или интерфейса использования). Два взаимосвязанных метода (захват и освобождение) получаются не связаны между собой в коде программы.

А вот если бы мьютекс был реализован как объект ООП, тогда такой ситуации возникнуть просто бы не могло, так как сам компилятор стал бы автоматически управлять взаимозависимыми вызовами одного и того же объекта (например за счет применения какого нибудь std::lock_guard).

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

Кроме того, в руби это объект. Да и в джаве это почти объект.

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

Использование концепции ООП позволяет (в теории) обращаться к объекту как к одному целому и контролировать логику его использования, например за счет отказа от вызова отдельных методов (lock и unlock) в пользу применения объектов владения и в этом случае правильностью вызова блокирующих и разблокирующих методов будет управлять компилятор (например, вот так).

  • Гарантировать отсутствие циклических ссылок можно только путем их запрета на уровне типов (определений классов).

Поскольку это в принципе абсолютно неверно, дальше я читать не стал, прошу прощения.

И что в этом не верного?

Кроме этого одного конкретного слова, других возражений нет? :-)

в этом случае правильностью вызова блокирующих и разблокирующих методов будет управлять компилятор

А, я понял, наконец: вы предлагаете решить проблему гонок запретом на параллельное выполнение кода, оперирующего одними сущностями, то есть, вернуться в однопоток. Перхоть усекновением головы, иными словами. Нет, спасибо.

Наверняка. Причём, оба товарища

Сниппет с кодом могли бы и получше придумать, если уж так хочется ООП прополоскать. Потому что ООП там не видно. Видно, как одна функция вызывает другие. И видимо под капотом идет общение с базой. Насколько я понимаю, антиппаттерн read-modify-write может стрельнуть и в ООП и в модели акторов. Один актор сам с собой не будет конкурировать, но 2 актора на разных потоках вполне могут.

У меня и в мыслях не было полоскать ООП. Этот пример показывает, почему инкапсуляция методами не такая уж и инкапсуляция. Ну замените там вызовы БД записью в лог, ничего же не изменится.

2 актора на разных потоках вполне могут [конкурировать]

Никто в здравом уме не станет создавать два актора на одну сущность.

Этот пример показывает, почему инкапсуляция методами не такая уж и инкапсуляция. Ну замените там вызовы БД записью в лог, ничего же не изменится.

Есть разница между шарингом состояния самого объекта, который действительно защищен только честным словом. И шарингом состояния внешней системы: БД, лога и т.п. Это уже вне рамок языка, фреймворка или парадигмы.

Никто в здравом уме не станет создавать два актора на одну сущность.

Ни один истинный шотландец...

[…] нагадить в БД можно в любых условиях. Был бы доступ.

Ну вот в решении, под которым мы ведем обсуждение, ставшем темой моей предыдущей заметки, у прикладного (пользовательского) кода — доступа на запись в базу нет. А состояние самого объекта, которое попадает в базу когда надо библиотеке, — защищено на 102% от постороннего вмешательства, вы даже хаками не сможете его изменить.

Почему бы? Акторы имеют только локальные данные и посылку сообщений наружу, так что не могут.

Только локальные данные? А баланс из примера кто хранит? БД? А БД на чем написана? Ну если ООП язык то понятно, "фу, гамно... " и т д и т п. А если нет, и это тоже актор, то тогда как без мьютексов, семафоров, либо других примитивов будут синхронизироваться поступающие данные?

Предвосхищая возможный ответ "этого там не нужно, в эрланге всё асинхронно" e.t.c, допишу дополнение: Если что, все эти эрланги работают поверх ОС, которая "внезапно" использует очереди запросов и "внезапно" снова на мьютексах, критических секциях и вот этом вот всём, так не любимом функциональщиками. Надо признать, что обе самых больших ОС написаны на Си, который не разу не ООП. Но написаны в ООП стиле (особенно windows), где пользовательскому приложению предоставляется АПИ для создания и манипулирования, о Боже!, указателями на инкапсулированное состояние, т е по сути объекты! (Без наследования. Да, больше похоже на объекты в стиле Алана Кеч, но претензии автора поста и особенного его пример с таким же успехом и на такие объекты распространяются. Он же не конкретизировал...)

А что, в "языках с мьютексами и ООП" эта БД будет внутри тоже лочиться? Нет, всё так же сетевое соединение? А тогда какая разница, на чем она написана, если любое сетевое взаимодействие ведт себя как актор?

Насчет "всё равно поверх мьютексов и прочего" - а это на самом деле не важно. "Все эти эрланги" не обязаны работать "поверх всё равно такой вот ОС", а предоставляют необходимую абстракцию. А работать они могут - и на самом деле для этого и создавались - поверх совершенно других вещей. Например, телекоммуникационного оборудования, типа свитчей, где обычно применяется другое железо - как минимум CAM, а то и Hardware Transactional Memory почему бы не поставить? И всё, уже никаких мьютексов, семафоров с критическими секциями уже нет в принципе.

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

обычно ооп понимали по мануалу разница c и c++

Все верно. Именно проблемы с многопоточностью в Java подвигли Рича Хики на создание Clojure. Туда же вскоре двинулся и Роберт Мартин.

Проблемы с многопоточностью в джаве уже довольно давно были решены правильно, см. Akka.

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

Разумеется. Но и вот прям совсем никто не помогает, правда? Несмотря на заявленную инкапсуляцию и прочие плюшки.

Сделать объект однопоточным? Гениально!

Объект эксклюзивно владеет своей памятью и не допускает конкурентный доступ к ней, да.

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

Обучите свою нейросетку современным ооп языкам, чтобы не галюцинировала.

Так это же медленно

Не более медленно, чем любой другой способ работы с разделяемой памятью.

То есть в ООП-шном подходе вы в лучшем случае получите ошибку компиляции, как в Rust. Собственно ваша критика сказанное в статье не отрицает, а подтверждает: "Классическое джаваподобное ООП не поддерживает многопоточность."

Вполне логично, потому что полагается на мутабельные структуры. А если эти самые мутабельные структуры из него выкинуть, то окажется, что и методы доступа не очень нужны. И получится ФП.

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

синхронзированный объект

Это тоже инструмент, не голый ООП

Причём, есть такое только в Java как я понял.

Это будет совсем не акторная модель уже. Тем более смешно тем, что изначальное ООП как раз и было тем, что сейчас называют акторной моделью.

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

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

1. Однопоточная очередь, классика ООП (Мьютекс): Плохо масштабируется. Становится узким местом.

2. Разные сущности (Иммутабельность): Отлично масштабируется для чтения, хорошо для записи.

3. Хитрые блокировки (Оптимистические блокировки): Лучший выбор для смешанных нагрузок, но деградирует при частых конфликтах

Оптимистическая блокировка лучше всего реализуется в ФП в STM. Она сильно зависит от деталей внутренней реализации, т.е. абстракция протекает и в общем случае сложность не оправдана.

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

Да, это хороший способ.

Исключительно lock free, без мьютексов нельзя сделать некоторые задачи.

Перевод денег с одного счета на другой к ним относится. Либо мьютексы и их производные либо eventual consistency. Может, ещё STM

Исключительно lock free, без мьютексов нельзя сделать некоторые задачи

Само собой. Но там, где требуется повышенная эффективность, лучше делать lock free (при условии, что это не повысит вероятность багов — а в этих штуках они допускаются легко и потом сложно отлавливаются)

Кажется, в обсуждении появился профессионал-практик :)

А то тут пока только теоретики выступали.

Проблема ООП в многопотоке не в таких простых примерах как счетчик.

Правда, абстракция ООП протекла, но что мешает сделать мьютекс руками?

Но сложность и риски реализации эффективных блокировок быстро растет со сложностью объекта.

Пример могу дать, но из LLM, pardon

Да что вы все к примеру-то прицепились? Ну да, пример тривиальный, но суть-то раскрывает ведь?

Конечно, в жизни ошибка будет заковыристее.

Давайте свой пример, чего уж там.

Проблема не в примере, а в выводах, которые вы делаете на основании этого примера.

Да ну? Я делаю вывод: заявленная инкапсуляция — ложь. Демонстрирую это примером. В соседней ветке вам то же самое демонстрируют более навороченным примером с комментариями от LLM. Но вы не понимаете, о чем вам говорят, и продолжаете упорствовать.

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

Понятно, нормально общаться вам корона мешает.

Мне ничего не мешает, всем до единого неглупым людям мой стиль общения весьма импонирует.

Я делаю вывод: заявленная инкапсуляция — ложь.

Очень странный вывод. Инкапсуляция - это сокрытие, и оно вот прям в вашем примере есть, и даже работает.

Ну началось. Окей, давайте поанализируем то, что в Вике написано:

Термин «инкапсуляция» может означать следующее в разных языках программирования:

  • механизм языка, ограничивающий доступ одних компонентов программы к другим;

  • языковая конструкция, связывающая данные с методами для их обработки.

Слово «инкапсуляция» происходит от латинского in capsula — «размещение в оболочке». Таким образом, инкапсуляцию можно интуитивно понимать как изоляцию, закрытие чего-либо инородного с целью исключения влияния на окружающее, обеспечение доступности главного, выделение основного содержания путём помещения всего мешающего, второстепенного в некую условную капсулу (чёрный ящик).

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

А теперь про второе, давайте посмотрим интересный пример (договариваемся сокрытие не использовать, все public).

class RectOrdPoint { // точка на определенной диагонали квадрата
  public int coordX;
  public int coordY;
  public void setCoord(int coord) {
    this.coordX = coord;
    this.coordY = coord;
  }
  public void setX(int coord) {
    this.setCoord(coord);
  }
  public void setY(int coord) {
    this.setCoord(coord);
  }
}

Ну вот у нас абстракция над точкой на диагонали в системе координат квадрата. Что мы знаем? При смещении точки по любой оси, она должна сместиться и по другой (иначе перестанет принадлежать этой диагонали).

Вроде все норм, с методами все отлично. Осталось только понять, кто запретит мне, как потребителю этого класса ручками поле поменять? Мы напишем в заголовке "отдельно координаты менять нельзя, координаты должны относиться друг к другу по формуле x=y". Ну вот это буквально "текущая абстракция", которой тут все пугают.

Как же защититься от такой закавыки? Сделать так, чтобы координаты могли меняться только совместно. Ну, например, объявить поля приватными, ибо "нефиг лезть". Я других действительно действующих механик не вижу, а конкретно эта называется "сокрытие".

Вот и выходит, что в отрыве от сокрытия в том или ином виде инкапсуляция в дикой природе почти не встречается. В концепции ООП, особенно в исходной формулировке Алана Кея, именно сокрытие и является краеугольным камнем.

«ООП для меня означает лишь обмен сообщениями, локальное сохранение, и защита, и скрытие состояния, и крайне позднее связывание». Алан Кэй

Из "троицы" наследование-инкапсуляция-полиморфизм в основе ООП только инкапсуляция и есть, причем в виде сокрытия.

Окей, давайте поанализируем то, что в Вике написано:

Очевидно, тут написана глупость. Читайте предыдущий параграф про отличия инкапсуляции и сокрытия.

кто запретит мне, как потребителю этого класса ручками поле поменять?

А что вам мешает выпрыгнуть в окно?

«Я не понял» ≠ «Очень странный».

Я не понял

Ну, хоть признаешь, уже хлеб)

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

Кричать о том, что не работают гарантии, которые вам никто не предоставлял - странное.

Тыкать будете своей бабе, когда таковую заведёте.

Ну здесь вы перегнули палку. Никого он не тыкал

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

ПыСы: все я понял, у Вас там ниже вообще войнушка пошла)))

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

Ну, хоть признаешь, уже хлеб)

Это какое на ваш взгляд число, единственное (ты), или множественное (вы)?

Кроме того, я написал довольно чётко, что «я не понял, как этот вывод следует из» — это не то же самое, что «этот вывод — странный». Господин, очевидно, даже эту простую сентенцию понять не осилил, а ухватился на слова «я не понял», почему-то решив, что я вышел с каминг-аутом.

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

А-а-а, этот момент. Понял.

я там дальше ниже читал-читал, читал-читал, но ваша войнушка так далеко зашла, что плюнул и вышел:)

Добро. Желаю удачи все нам уже разобраться один раз с этим дерьмом.

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

Все синьоры заняты в суровом проде, и некому разрабатывать стандарты.

Две школы засели по своим лагерям, и строят костыли системам придуманным до 90х и соазу после.

Железняки нам подари многопоток, и кодеры вместо этого нет придумать новую парадигму, но все заняты костылями. И Web, конечно тут постарался со своими палками)

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

Лучше и понятнее ситуацию и не охарактеризовать, пожалуй.

Прям хоть в камне отливай, и я совершенно серьёзно. Не зря один из Владимиров Владимировичей говорил, что большое видится на расстоянии.

Перечитайте, я там отредактировал. Ну и в закреп)))) если считайте так же.

Внатуре, я давно за этим слежу

;))

Не иакой я и тупой окащывается)))

Все уже давно придумано, реализовано и академиками с пейперами доказано ;) Добро пожаловать в мир Haskell!

Надо будет проверить;), что это вы мне за чудо-юдо рекламируете:))) Rust, тоже говорят, много от утечек памяти спасает. Еще там чего…

Ток я не об этом, да. Просто хотел добавить, что тут опять-таки вина индустрии, но уже с другого бока;)

Ну что тут скажешь — все так), говорят мужики.

… Ну вот так все и получается, мы крутимся вокруг этих частных случаев. Спорим в прокуренных кофейнях)

А толку, если индустрию штормит. Это тебе не ракеты строить, и автомобили проектировать, конвееры. Неее, нифига. Тут здесь и сейчас. От клипового мышления к гонкам на рынках акции, а не как вы подумали за очередью к такту процессора или состоянию значения обьекта)

ой куда это я уже.

Раст ооп, хаскелл фп. Круто шо! тока даавайте вернем 2007 год — сайты попроще, мессенджеры, чтобы звонить и передавать файлы, облака, что хранить онлайн, соцсети опустошить от всего беспредела. Ну короче. Были и плохие вещи в 2007, но можно, уже както надавать дизайнерам по щекам, чтобы не грузили интерфейсы. Ну столько бреда лишнего генерируется и качается насосами по трубам. И так далее.

PS: у вас сеньеров и так жизнь в шоколаде, вы бы профсоюз может быть открыли, забастовки какие, против бреда дизайнеров и маркетологов и дед-лайнов бы устроили.

Вся надежда на интеллегенцию.

Только возвращаться нужно в 95-97. Появление Java и JavaScript ;) А ведь хотели Scheme затащить в браузер, но не срослось

Не спорю, я так ляпнул, для красна. А я бы может во времена тюринга вернулся. Хотяяя чо там у нас. Троичная логика так и не взлетела)

Раньше. Видел мнение, что Веб покатился с каким-то нововведением где-то в 94, и в IPv6 все тоже перестали обсуждать тему адресов короче 128 бит где-то в 94.

против бреда дизайнеров 

Мы, и сами, своего рода, дизайнеры!

Читаю и ржу. То есть они "изобрели" weak references на десятилетия позже, чем Perl ? Инновации! Текнолоджя!

Да, и Rust тоже, там влияние OCaml было

Вот этих всех влияторов, — надо бы уже как-то разобраться с ними)

/сарказм.

А шо поделаешь, если индустрию несет. Мир вокруг вобще сошел с ума. Бардак куда не глянь. Я того рот внатуре. Простите

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

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

И что нибудь из этого «мусора», да и пригодится нам в будущем.

А разгадка одна - безаблагодатность капитализм.

Все так. И есть адекватные капиталисты, типо немцев, а есть все остальные (условно говоря)

Немцы были адекватными до примерно 2000 года, потом у них произошло озеленение головного мозга. Сейчас этот строй — один из самых гнилых в ЕС.

Согласен соглаен, но эта повестка, если и не от амеров пошла, то по-любому отравила мозг всем кому не попадя.

Этих примеров «дегенерации» можно много привести. Но как не глянь, их педантичность любовь к порядку в делах, всегда при них. Вся немецкая Европа в широком смысле тому пример, а потом сравните с югом)) и даже в центре и на востоке в Польше их влияние и культура видна. Чехия, Польша, тем более Прибалты и северные немцы. Ну не придраться к ним. Да налоги огромные, но живут в своем условном комунизме, никого не трогают, тихо создают безумно крутые вещи.

А амеры с итальянцами, они другие. Тоже хреначат шо дурные, но с пафосом, сумбуром, высасыают все соки из работников. Пользуются грязными схемами. И тоже мать их преуспели, и очень даже умнички. Но конда они начинают перегибать палку с разными повествками, глобально их навязывать и так далеее. Ну вот мы пришли в пункт где мы находимся: добро пожаловать в новый дивный мир.

PS: Важно понимать, что не все так страшно, как кажется, и выход всегда есть. Главное не потерять ажкватность, быть сплоченными и однажды стрельнуть. Ктотна пенсии в институте, кто топ менеджером… и так далеее

А анклав коммунизма (OSS) возможен только внутри капитализма, иначе нам жрать будет нечего, так что я даже не против капитализма, хотя жить выбрал в максимально социалистической стране мира.

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

Главное, цивилизацию не посрать, а каждому в отдельности нормально саморазвиваться в меру естественного физиологического потенциала.

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

В OSS не буду углубляться, но мессаж Ваш понятен и частично правильно сформулирован.

Но я бы сказал, добавил, что да — сейчас наша цивилизация все еще находится в поиске стабильной полит-экономичской модели, но мы смело уже можем заявлять, что накопленные опыт и знания нам дают огромный инструментарий и осталось лишь просто сложить все едино, формализовать (сформулировать), регламентировать и исполнять (с учетом постояной эволюции всех процессов. Это тоже закладываем в концепцию. Доктрину).

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

вот тебе и библия. А не вот это вот все про радикальный фанатизм разного рода: от экстра либеральных идей до ультраправых.

БАЛАНС всему голова.

Лучей добра Вам.

До меня тут еще одна идея догла в конце-концов.

Как я понимаю, в чистом и первозданом замысле, что ооп, что фп, сотвсеми кго современными костылями, вполне можно использовать в самом наварочегом проде. Но возникает проблема, — и это прокладка в виде. ОС.

Требуется: создать нормальный консорциум по заклдаке и реализации стандартов ОС. К примеру консорциум Wi-Fi alliance.

Задется какая-то бизнес-индустриальная цель, и несколько комманд уважаемых в цеху его осуществляют и снимают сливки естественно.

то есть, майкам, придется перстроить свои бизнес процессы от: Мы задаем стандарты, к : мы реализуем заданные стандарты.

Все это упаковывется в какой-то уровень 7.1 (по аналогии с сетевой моделью OSI). Зачем ?

Что бы приеончить этот бардак в очень важном слое взаимодествия

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

Но они сопротивляются этому развороту. А лоби такого нету.

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

Какой-какой памяти? Что за OBEMLV-память

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

Не уверен что точно указал на то что я имею в виду.

Инкапсуляция — это вложение. Как матрешка 🪆, пример из жизни.

При несложных объектах (типа счетчика) разницы в скорости почти нет. Затраты и сложность на отправку сообщения в Akka и на захват мьютекса сопоставимы.

При сложных объектах всё кардинально меняется.

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

Почему для сложных объектов ООП с блокировками — это боль

Представим сложный объект "Банковский счет" с методом Transfer(BankAccount other, decimal amount).

1. Проблема композиции и Deadlock

  • ООП с локами:

    • Поток А вызывает account1.Transfer(account2, 100). Метод Transfer внутри себя делает lock(this) (т.е. на account1).

    • Далее ему нужно изменить account2. Он пытается сделать lock(account2).

    • В это же время Поток Б вызывает account2.Transfer(account1, 50). Он захватывает lock(account2) и пытается захватить lock(account1).

    • Результат: классический deadlock.

  • Решение? Ужасное. Нужно вводить глобальное правило порядка захвата блокировок (например, по ID счета). Абстракция "просто вызови метод" сломана. Вам нужно знать о глобальных правилах, чтобы просто перевести деньги.

2. Проблема гранулярности блокировки

  • ООП с локами:

    • Метод GetHistory() должен блокировать весь объект? Или только коллекцию с историей? А ChangeAddress()? А GetBalance()?

    • Вы начинаете вводить несколько объектов-блокировок внутрь одного объекта: balanceLockerhistoryLocker_addressLocker.

    • Код превращается в минное поле. Забыли взять нужный лок — получили data race. Взяли не в том порядке — получили deadlock. Абстракция инкапсуляции трещит по швам, наружу торчат детали реализации синхронизации.

3. Проблема блокирующего I/O

  • ООП с локами:

    • Что если в конце Transfer нужно записать транзакцию в базу данных? Делать это внутри lock — самоубийство. Вы заблокируете оба счета на время долгой сетевой операции.

    • Делать это после lock? А если запись в БД упадет? Как откатить изменения в счетах, которые уже "отпустили"? Это требует сложнейшей логики компенсаций.

Как Akka решает эти проблемы для сложных объектов

Akka заменяет прямые вызовы методов асинхронными сообщениями.

  1. Нет Deadlock'ов: Нет блокировок — нет дедлоков. Поток А шлет сообщение Withdraw актору account1. Тот обрабатывает его, меняет свое состояние и шлет Deposit актору account2. Никто никого не ждет на блокировке.

  2. Гранулярность "из коробки": Каждый актор — это единица гранулярности. Сложная система разбивается на много мелких, простых акторов. Вам не нужно думать о _balanceLocker, у вас есть BalanceActor.

  3. Асинхронность "из коробки": Операция с I/O — это просто еще одно асинхронное сообщение. Актор account1 отправил сообщение в DatabaseWriterActor и забыл про него. Он свободен принимать новые сообщения. Когда БД ответит (успехом или провалом), DatabaseWriterActor пришлет ответное сообщение. Это идеально ложится на асинхронную природу реального мира.

Вывод

Для сложных, взаимодействующих систем подход Akka несравнимо лучше, потому что его базовая абстракция (асинхронное сообщение) гораздо лучше моделирует конкурентный мир, чем абстракция ООП (прямой вызов метода), которую приходится "чинить" костылями в виде блокировок.

Вместо вопросов "метод GetHistory() должен блокировать весь объект? Или только коллекцию с историей? А ChangeAddress()? А GetBalance()?" Akka решает это не улучшением блокировки, а уничтожением самого понятия "сложный объект". Принцип: одна обязанность — один актор. Вы не создаете один гигантский актор BankAccount, который делает всё. Вместо этого вы разбиваете его на несколько мелких, специализированных акторов.
...

... ну в этой части мне самому надо разобраться прежде чем копировать LLM

... ну в этой части мне самому надо разобраться прежде чем копировать LLM

боюсь мои комментарии к вашему фейспалму будут слишком токсичными

вы пока ничего толкового не утверждали, показывали только свои вопросы и непонимание

В место того, чтобы подробнее раскрыть свою мысль, вы копируете портянки LLM, которые сами до конца не понимаете. Это вообще нормально? Может мне тоже через LLM вам начать отвечать? :-)

Портянка выше появилась здесь по моей просьбе. @Dhwtj отвечал мне. Предварительно испросив разрешения у автора текста на портянку от ЛЛМ.

Вы забываетесь, юноша.

Ну что-же, самое время напомнить вам про ваш же собственный комментарий

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

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

А чё, молодец ваша ЛЛМка, столько воды налить в то, что я в один абзац уместил! Если стало понятнее — я только рад.

Принцип: одна обязанность — один актор. Вы не создаете один гигантский актор BankAccount, который делает всё.

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

LLM подстраивается конкретно под меня, мне понятно. Более того, если мне не понятно я прошу переделать ответ. Ну вот так... Наверное те кто больше в теме могут думать более кратко, через красивые абстракции

Смысл не делать гигантский актор в том чтобы не блокировать его целиком надолго. Всё равно блокировки там внутри есть. Неявные, через логические зависимости, но есть

В классической модели — блокировок, как таковых, нет. Есть гарантия, что пока не выполнилась обработка сообщения, новая не запустится. И есть «почтовый ящик сообщений», в который будут падать все входящие.

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

Очаровательно-инфантильный подход на особенно удачном примере "банковских транзакций" (к которым вас с вашей ЛЛМ на пушечный выстрел подпускать нельзя)

По порядку:

Представим сложный объект "Банковский счет" с методом Transfer(BankAccount other, decimal amount).

Зачем мы его представим? В продакшене мы такое никогда использвоать не будем. У нас будет достаточно простой объект BankTransaction(decimal amount, BankAccount from, BankAccount to). Целиком всю высосанную из пальца проблему решит.

  1. Проблема композиции и Deadlock: ее нет, это транзакция, она одной операцией выполняется. Мы лочим оба баланса разом.

  2. Проблема гранулярности блокировки: ее нет. У нас нет сложного объекта. Есть объект BankAccountBalance, есть BankAccountHistory, есть BankAccountOwner - буковка S из замечательной аббревиатуры SOLID. И мы всегда знаем, что лочить.

  3. Блокирующий I/O... Кхм, тут только цитировать:

Что если в конце Transfer нужно записать транзакцию в базу данных? Делать это внутри lock — самоубийство.

Самоубийство - это писать банковскую проводку в БД вне лока транзакции. Вот тут прям настоящее самоубийство.

Предлагаемое вами решение - эпический фейл.

Вы предлагаете Аккаунт1.СписатьДеньги -> Аккаунт2.ЗачислитьДеньги -> ПисательВБД.ЗаписатьВБД. Теперь, внимание, вопрос: с первого аккаунта деньги списались, а Аккаунт2 зафейлился. Чо делаем? А если оба ОК, но БД упала - чо делаем? Если упростить, то буквально:

 Как откатить изменения в счетах, которые уже "отпустили"? Это требует сложнейшей логики компенсаций.

Ах, блин, это же в недостатках логики с локами написано! Или ваша модель ортогональна решаемой проблеме (и более того, абсолютнейшим образом не подходит для ее решения)?

Мы лочим оба баланса разом.

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

transfer( Account source, Account target, Decimal amount )
  synchronized( source, target ) {
    source.ballance -= amount;
    target.ballance += amount;
  }
}

Да, пардон, тут я глупость сморозил, конечно.

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

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

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

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

Вас мало повозили вчера мордой по грязи, что вы опять тут вылупились?

Разве? Это вам и вчера и сегодня популярно разжевали, что у вас нет понимания ни в ООП ни в БД (о чем вы сами и написали). А после нескольких высокомерных закидонов с вами и другие нормальные люди перестали общаться и осталось вам только переписываться с LLM :-)

у вас нет понимания ни в ООП ни в БД (о чем вы сами и написали)

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

Вы считаете, что данные могут храниться только в БД и других мест не может быть. Я правильно понимаю? И о долгоживущих Erlang процессах, которые могут быть запущены годами и хранить внутри себя состояние, вы тоже, судя по всему, не в курсе.

Мне очень интересно наблюдать, как людей корёжит, стоит слегка отойти в сторону от привычной парадигмы.

Сам придумал тезис, сам с ним поспорил. Вы уверены, что именно вас таким образом "не корежит"?

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

Никакой "тезис" я не придумывал.

Ну как же? Вы считаете, что данные могут храниться только в БД и других мест не может быть. Я правильно понимаю?

Я не считаю, что данные могут храниться только в БД.

Место хранения внешних данных, это обычная абстракция с API для обращения к этим самым данным. А уж где они физически хранятся, в БД, процессе Erlang, который живет уже 40 лет в виртуально машине, распределены между разными узлами или вообще вычисляются на лету по мажоритарному принципу, это дело десятое.

Место хранения внешних данных, это обычная абстракция с API для обращения к этим самым данным.

Рыдаю. А чё только одна абстракция, может понадёжней будет ну штук пять взять?

И это, что такое «внешние данные»? Насколько имя пользователя для банковского приложения — внешнее?

И это, что такое «внешние данные»? Насколько имя пользователя для банковского приложения — внешнее?

Вот это вы странное спрашиваете. В контексте беседы "внешние данные" - это все данные, хранящиеся не в памяти приложения.

А если я его храню именно что в памяти приложения, но иногда (при изменениях) записываю в некое персистентное хранилище (не обязательно СУБД)?

Но для всех операций в коде оно хранится строго в памяти приложения?

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

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

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

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

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

Конечно. А к чему этот пассаж?

Если это как-то касается меня и моего текста — я не фанат ФП, не топлю за ФП, и не считаю ФП панацеей от всех бед.

Вот тут вы несколько несправедливы. Беседа таки в ключе исходной статьи ведется, а в статье таки в качестве примера BankAccount приводится. И вот хранить стейт банковских счетов в "долгоживущих Erlang процессах" (т.е. инмемори, я же верно понимаю) - ну вот такая себе идея.

В исходном ключе обсуждения таки да, данные о состоянии банковских счетов могут храниться только в БД. Мало того - еще и обязательно в транзакционной!

Не ткнёте пальчиком в то место исходного текста, где упоминается банковский аккаунт? Заранее спасибо.

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

хранения счетов в ивентлогах

Все равно, снапшот надо делать иногда

Каких еще счетов, нафиг? Где в исходном тексте хоть одно слово про счета?!

в статье таки в качестве примера BankAccount приводится

Так чего, Господин Фантазёр, покажете, где именно в тексте я хоть что-то говорил про банковсие аккаунты, или признаете, что заврались вконец?

да я это писал изначально пару дней тому назад

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

Зато мнение имеете.

Реальных проектов без базы с транзакциями не бывает? Ясно.

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

Был бы другой пример, другой бы разговор был. А для банковских счетов есть 2 неотъемлемых требования:

  1. Транзакционность

  2. Последовательность операций.

Требование параллельно 2 операции по одному счету выполнять - ересь, высосанная из пальца. На банковские транзакции ФП ложится отвратительно, не надо его тащить. А ООП вы не понимаете, от слова "вообще"

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

Приведите пример счетов и банков без транзакций(что бы я ими не пользовался)

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

Я думал хотя-бы на тему блокчейна пофантазируете))

любая с конечной согласованностью подойдет

Буду краток: нет.

и событийная туда же

Еще раз: нет.

банки" бывают с разными требованиями

И еще раз: нет.

банки это не только перекладывание денег - в перекладывании со счета на счет я бы поостерегся от конечной согласованности

выражайтесь точнее

Да блин куда уж точнее: "проектов по управлению балансами банковских счетов"

Управление балансами (читай, изменение циферки на счету) банковских счетов.

Ну ок, сократим формулировка: банковские транзакции.

Почему же? На самом деле она ровно такая и есть, несмотря на то, что внутри у (каждого участвующего) банка Oracle с транзакциями. Ну вот просто открываю я детализацию по своим покупкам, если мне интересно, и вижу, что деньги за вчерашнюю еду в супермаркете на самом деле висят в статусе HOLD. Их отпустит, когда пройдет клиринг (или как-то так, не помню уж точно). То есть для конечных юзеров - что меня, что магазина - создают впечатление, что деньги перечислились мгновенно и транзакционно. По сути, это оптимистический подход в создании видимости, а на самом деле там работает более сложная логика, которая может откатить это обратно через пару суток (а то и больше, если кто-то откроет диспут).

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

Их отпустит, когда пройдет клиринг 

вроде, от такого отказываются уже
а то можно вспомнить понятие «банковский рейс» — комплекс операций по приёму и обработке платежей, которые проводятся несколько раз в день в соответствии с определённым графиком.

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

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

Могу только примерно. Я мало отношения к банкам имею.

Можно делать платежи напрямую (золотом, гыгы), а чтобы на каждый платёж не возить золото можно делать расчет через клиринговый центр, который осуществляет как бы кэширование записей и реальные расчёты в конце торгового периода, дня или рейса (лет 300-400 назад это был физический рейс с золотом). Между странами специальные клиринговые центры, внутри через ЦБ.

Каждая транзакция через ЦБ обрабатывается индивидуально (это и есть «валовые расчеты» / gross settlement). Это долго и дорого, применяется к большим суммам.

Небольшие платежи ЦБ может реально завершать быстро, через систему быстрых платежей. Такие есть ещё свои у крупных банков. Ну и на закуску прямые переводы (неттинг), если банки доверяют друг другу а потом рассчитываются через клиринг.

ЦБ это гарант и финальное юридическое подтверждение платежа. На нём транзакция заканчивается. А все эти быстрые переводы к которым вы привыкли это ещё не завершенная транзакция, платеж можно оспорить и вернуть.

Как работают карточные платежи и палка и почему такой платеж можно оспорить чуть ли не спустя месяц - я не знаю.

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

Лочим обе записи это если между своими счетами в одном банке. Да и то, не лучшее решение

А вы устанавливаете транснациональные законы написания банковского ПО, или просто попердеть в лужу зашли?

Потому что ну вот я скажу: да, и еще раз да. В РФ мне с банками работать не доводилось, зато могу много порассказать про Европу и США.

Любой эквайринг в каждом втором американском банке.

Eventual consistency?

Там есть ненулевая вероятность не прийти к согласованности никогда. Хотя, вероятность может быть очень низкой и приемлемой далее в финансах. Но надо уметь считать риски. Интересно, как оно там

Да вот ровно так.

До определенного порога платежа — просто лог, для списаний, которые выше этого порога — пин на терминале + настоящая транзакция.

занятная логика, в копилку

Странно как-то, почему-то в статьях они обсуждают что выбрать SQL или NoSQL, ну и очевидно пишут что SQL с его транзакциями лучше всего подходит для обслуживания денежных переводов)

Эти мифические «они» — это кто?

Получается у них в половине вузов учат использовать SQL и NoSQL, а другая половина изучает ваш подход?))) (исходя из вашего утверждения что каждый 2 банк без баз данных в америке)

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

В продакшене мы такое никогда использвоать не будем

Лейстрид тоже был очень полезен как человек начисто лишенный воображения ©

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

Проблемы ООП с многопоточностью начитаются с определенного уровня сложности модели. На простых можно решить вручную. На сложных придется использовать ФП или акторную модель.

Простой пример от автора тоже не годный.

...только хотел плюсануть (всё верно, за вычетом того, что я не знаю Akka), и тут ВНЕЗАПНО всё портит LLM.

ладно, не буду больше в LLM

буду гуглить и постить другую глупость, человеческую

почему гуглить? уж очень термины все в программировании нечеткие, просто бесит

"по определению дяди Боба микросервис то, а по определению тети Сони сё"

Увы, такова природа человека, это уже к психологии и мозгу, с которых всё на самом деле и начинается, а не с формальной математики...

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

Как обсуждение ООП так все бросают друг в друга ссаными тряпками

Да чтож такое опять?

Лол

Хм, ну ведь можно же без портянок LLM мысли формулировать? :-)
Причем под ними я подписываюсь на все 146%

LLM я знаю что читать не очень приятно, о чём я предупредил. Но тяжело сформулировать сложную мысль на бегу. Я же не сижу за компом целый день. Не хотите не читайте. Всё честно.

А вы не изволили сформулировать ничего

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

Удваиваю. Ощущение, что за несколько недель комментарии на Хабре превратили в склад нейрослопа, где каждый неразобравшийся в теме гадит выводом ллмок. Спорить с ними толку нет, ибо знаний в них нет, и то что написала ллм они не понимают/не видят ошибок.

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

Два вызова метода increment из двух разных потоков приведет к тому...

два раза это два раза (с) и уже не важно сколько там было потоков.

Это же гонка, результат не однозначен.

Что забавно: в однопоточных языках (JS, python, PHP) объекты не очень-то и нужны. Они там выглядят как что-то инородное.

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

Но вот именно руби не претендует на все эти умные паттерны типа «инкапсуляции» и прочих. Там даже приватные методы можно задокументированными средствами языка вызывать извне. Можно подменить класс Integer, чтобы он хранил текстовые представления строк на суахили. Можно буквально всё. И именно за это я его люблю. Вот в такой парадигме — ты программист, ты умный, ты разберёшься, и ты ответишь, если что — мне ООП очень нравится.

Ну приехали. И как же писать на JS без объектов?

Изначально прототипное наследование. ООП (классы) только потом прикрутили

Ок, объекты были. Классов изначально не было.

Технически, JS разделяет объекты и массивы, а массивы, очевидно, диффеоморфны объектам. Voilà.

Вот как раз технически они ничем не отличаются.

Семантика добавления элементов абсолютно разная, и, как следствие, объект тащит за собой хэш-таблицу, которая массиву не нужна. Размещение в памяти разное, прототипы разные. Но, конечно, можно трактовать слово «технически» достаточно широко, например: и те и другие являются примитивами языка.

Вы опять рассуждаете о том, о чём не имеете ни малейшего представления. Как таковых массивов в JS вообще нет. Есть лишь объекты с особым поведением свойства length и куча костылей в компиляторе, чтобы это не так сильно тормозило в большинстве случаев.

Я человек простой: пошел да посмотрел в код ноды. Посмотрите, как v8::Array в принципе реализован; но, конечно, может быть я всё неправильно понял, и разучился читать код.

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

Это не первая попавшаяся функция. А по какой реализации надо судить? — Вы скажите, с удовольствием посужу по ней.

Мне бы код, а не беллетристику.

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

Если честно, мне не очень понятно, какие именно выводы предлагается сделать.

Мир катится в тартарары? — Дык тоже мне новость.

А давайте двум плотникам дадим один топор, и заставим их делать один табурет? Да не, пробовали, они дерутся все время за топор, мешают друг другу. Ну может дадим им второй топор? Еще хуже стало - пальцы друг другу поотрубали... Мораль-то в чем? А, виновато ООП.

Если вы не понимаете разницу между виновато и непригодно — мне жаль.

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

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

В тексте заявлено буквально следующее: в многопоточной среде больше половины так называемых «гарантий», краеугольных камней ООП, типа инкапсуляции и двух третей паттернов — превращаются в тыкву. Если вас это устраивает — флаг в руки.

Мне доводилось писать многопоточный довольно конкурентный код на руби с ручной обработкой пула потоков на си, и оно тоже работало. Понравился ли мне этот опыт и хочу ли я его повторить? — Нет, спасибо.

ООП решает другие проблемы. Инкапсуляция - для ограничения области видимости. Наследование - для переиспользования кода. Полиморфизм - для унификации интерфейсов. Никогда, нигде я не встречал обещаний, что ООП обеспечивает синхронизацию доступа к данным.

Не обеспечивает, а только мешает это сделать

Инкапсуляция - для ограничения области видимости

нет, вы читали не те учебники

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

Этот спор — это отражение эволюции языков. От "вот инструменты, делай правильно" к "ты не сможешь сделать неправильно"

Вашу проблему с плотниками Генри Форд ещё 100 лет назад решил.

Кстати вполне себе в ФП стиле.

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

Это ивентлог, безтранзакционный, на котором работают многие современные банки.

Потому что люди делятся на тех, кто видел ситуацию «стартовали из приложения транзакцию и тут отвалилась сеть», и тех, кто еще нет.

Конвеер это временнóе разделение, т.е. опять мьютексы.

Это, кстати, любопытный вопрос :)

Из конвейера очень просто сделать не временно́е разделение, достаточно рядом с каждым рабочим установить столько лент, сколько он в состоянии обслужить, тогда пока Вася перед Петей возится со своим болтом, Петя разгребает другую ленту, от Маши. Это уже фактически акторная модель. Жалко, что у меня нет своего автомобильного заводика.

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

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

Это неверно. Даже если вы целую докторскую защитили.

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

Это прекрасная метафора, спасибо!

Тут сам конвейер является "общей деталью" и требует мьютексов, семафоров и прочей машинерии синхронизации. Так же как и очереди доставки сообщений.

Чё?

Конвейер (и очередь) —  является из коробки примитивом синхронизации.

Разве что только если это wait-free очередь, где несколько потоков не работают одновременно с одной областью памяти. Но это довольно редкое явление. Зачастую доступ к очереди защищают мьютексом или cas-циклом и меняют общую память.

С точки зрения работника завода конвейер - это скорее среда выполнения. Которую он может как максимум остановить, но уж точно не повернуть в обратную сторону или переставить сидящих за ним сотрудников. Да, для наладчиков конвейера в нем есть возможность двигаться как хочешь, но эти наладчики конвейера и работники завода - это как разработчики ОС / JVM / BEAM / .Net и разработчики прикладного софта. Они один раз настроили конвейер / предоставили абстракцию, а люди дальше им пользуются.

Мьютекс обеспечивает эксклюзивность владения объектом. И нет никакой разницы, деталь это на конвеере, или стена в ремонтируемой квартире.

Или сам конвейер

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

Где-то будет барьер синхронизации по-любому, вселенная так устроена

Мне понравилось как в SICP на эту тему написано:

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

Автор у вас проблемы не с ООП, а с требованиями к коду. Если код должен что то безопасно сделать, то вы в своём коде должны эту безопасность обеспечить.

У вас требование безопасности есть, а в коде выполнения этого требования нет.

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

В SQL для этого делается блокировка записи через SELECT FOR UPDATE, или можно делать апдейт на конкретное значение, с проверкой того что текущее значение не изменилось:

UPDATE balance SET amount=150 WHERE amount=200 and user_id=123

Как то так надо выполнчть изменение данных. Если это переменнная в памяти, или байты в файле, то изменение значения надо выполнять с аналогичными проверками. Условие выполняется ? Да - тогда обновляем. А что бы ни какой другой поток переменную не поменял используем блокировки, вы это прекрасно знаете.

ООП это метод. Вы его применяйте с учетом всех требований и программа будет работать как надо.

А если ваш код не обеспечивает атомарности, то пеняйте сами на себя, а не на ООП.

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

Вообще поэтому объекты и нельзя использоаать как DTO, именно поэтому мы должны сказать объекту: уменьши баланс на 50, если текущий баланс 200. И объект сам разберется как ему это сделать атомарно с транзакционной целостностью.

Говорить объекту: установи баланс 150, это значит брать на себя ответственность за то что баланс должен быть 150. Когда вы так используете объект, то это не ООП, это процедурное императвное программирование.

Комментатор, у вас проблемы с пониманием несложных текстов.

ООП гарантирует инкапсуляцию? Если вы согласны, что нет — тогда у нас нет разногласий.

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

Вот это - гарантируется. Что не так?

ООП не гарантирует инкапсуляцию, ООП предоставляет некоторые средства, которыми можно достичь инкапсуляции (если хватит квалификации). Достичь ее можно и без ООП, с ООП просто удобнее. Как вы вообще себе представляете технически гарантию инкапсуляции в языке, в котором юзер может писать свои классы)

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

Да запросто: например, полная иммутабельность.

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

Достаточно ли полезен Whatsapp? А Discord?

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

У Whatsapp и Discord вообще нигде нет изменяемого состояния? Ни одной бд не используется? (она тоже изменяемое состояние)

один из тонны примеров

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

Иммутабельность — это свойство объектов языка, а не языка в целом. В полностью иммутабельном языке можно запросто менять состояние.

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

везде где можно менять состояние, можно написать такой код, который позволит менять это состояние тому, кто это делать не должен теми способами, которыми это делаться не должно) про это и есть инкапсуляция, а не про конкретные способы, которыми этот эффект достигается

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

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

Не позорьтесь

Хорошо, больше не буду)

Попробуйте включить абстрактное мышление и понять что делает акторная модель глобально, чем она фундаментально отличается от неакторной модели (может быть поможет подумать во что это в конечном счете компиляется и на чем исполняется), подумайте про Тьюринга vs Черча. Не знаю как ещё донести до вас элементарные вещи)

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

Язык может быть триста раз полным по Тьюрингу, но если кусок кода работает с базой по read-only соединению, то поменять состояние ему не удастся. Внезапно, правда?

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

Возьмите более современный компилятор. 2к25 год на дворе.

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

Кому должна?)))

из определения

вот видите, вы даже не видите

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

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

из определения

Покажите мне уже это определение, на которое вы ссылаетесь. Я ни одного подходящего не видел.

цель инкапсуляции - защита инвариантов

Нету у нее такой цели.

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

Я вам сейчас страшное скажу: в определении ФП тоже ничего про потокобезопасность нет. Оно вообще не для того придумывалось, и не те проблемы решало. Потокобезопасность - это следствие из ограничений, в которых существует функциональное программирование (вы не можете сломать разделяемое состояние, если у вас его нет).

И вы, ссылаясь на следствие из парадигмы ФП, утверждаете, что оно же должно быть заложено в определении ООП? Вы в своем уме вообще?

в определении ФП тоже ничего про потокобезопасность нет.

Есть. В ФП вообще нет одновременных потоков. Потому что времени нет.👅

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

Анловики

In software systems, encapsulation refers to the bundling of data with the mechanisms or methods that operate on the data. It may also refer to the limiting of direct access to some of that data, such as an object's components.[1] Essentially, encapsulation prevents external code from being concerned with the internal workings of an object.

Encapsulation allows developers to present a consistent interface that is independent of its internal implementation. As one example, encapsulation can be used to hide the values or state of a structured data object inside a class. This prevents clients from directly accessing this information in a way that could expose hidden implementation details or violate state invariance maintained by the methods. - тут выделил для вас

Иммутабельность — это свойство объектов языка, а не языка в целом. 

Вообще-то нет, это буквально основное отличие ФП и ООП, все остальные отличия - это следствия.

товарищ решил что приложение должно быть тотально иммутабельным
обычно это функциональное ядро, императивная оболочка

Ого, у вас в профиле написано «high load distributed systems»!

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

Зачем мне иметь представление об архитектуре вотсапа, вы думаете это единственный месседжер на земле, или что месседжеры это в принципе какой то ультраинтересный класс систем, о котором должен непременно иметь представление каждый программист?

Вы просто новые поды в кубер втыкаете, когда метрики зашкаливают?

Раскусили)

Бесплатный совет: высокомерие ещё никому не помогало найти общий язык с другими людьми)

Зачем мне иметь представление об архитектуре вотсапа […]

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

высокомерие ещё никому не помогало найти общий язык с другими людьми

С чего вы взяли, что я стремлюсь находить общий язык с другими людьми?

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

Я нахожу системы с изменяемым состоянием намного более интересными)

С чего вы взяли, что я стремлюсь находить общий язык с другими людьми?

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

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

ООП гарантирует инкапсуляцию?

ООП постулирует использование инкапсуляции. ООП это набор рекомендаций, ответственность за следование этим "правилам" / "рекомендациям" - это ответственность вашего кода.

ПДД предписывается движение на зелёный сигнал светофора, если вы ездите на красный, то пеняйте на себя. а не на ПДД :) Можно конечно требовать что бы каждый светофор был оборудован барьерными автоматами, но толку то ? Требуйте конечно, но решит ли это проблему ?

Я ничего не требую, в первую голову.

Я просто указал на то, что те механизмы инкапсуляции, которым все обучены, и которые предоставляются из коробки (наподобие «геттеры и сеттеры лучше прямого доступа к переменной») — рассыпаются в труху в конкурентной среде.

Откройте любую книжку по ООП, найдите там главу синглтон, и выполните предлагаемый код в кластере на N машин. Что получится? — N синглтонов.

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

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

Весь код по умолчанию однопоточный […]

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

Почему авторам эрланга 40 лет назад ничего не помешало сделать код по умолчанию не только многопоточным, но и размазанным по кластеру? Почему эрлангу всё равно, в каком потоке и на какой ноде выполняется функция, а какой-нибудь джаве из коробки (впрочем, умные люди уже принесли туда Akka) — нет?

в подавляющем большинстве случаев не представляет никакой сложности

У нас очень разный опыт в этом плане. Синхронизация тысячи независимых гринтредов на семафорах и мьютексах — это очень непростая задача.

Почему авторам эрланга 40 лет назад ничего не помешало сделать код по умолчанию не только многопоточным, но и размазанным по кластеру?

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

Синхронизация тысячи независимых гринтредов на семафорах и мьютексах — это очень непростая задача.

Тут нет никаких противоречий с тем что я написал)

в подавляющем большинстве случаев это никому низачем не нужно

Кхм. Боюсь, у вас очень поверхностное представление о том, что происходит в индустрии прямо сейчас.

Смотря о какой индустрии речь, я работаю в отечественных бигтехах последние 10 лет, тут ничего фундаментально нового не происходит)

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

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

Ну, может, потому что для подавляющего большинства задач однопоточность достаточна и при этом она проще? Давайте от однострочников на /bin/sh многопоточности потребуем. Да и закон Амдала никто не отменял.

для подавляющего большинства задач однопоточность достаточна и при этом она проще

Я не согласен с обоими утверждениями :)

Узок их круг, страшно далеки они от народа (с)

Я просто тоже так думал, а потом внезапно что-то понял, и мне стало проще в полностью асинхронном мире. Это как с заковыристым ударом на бильярде: вы триста раз пробуете какой-нибудь круазе, уже готовы шарами в стены начать кидаться… — а потом что-то щелкает, и всё: вы больше не понимаете, почему оно у вас раньше не получалось.

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

бильярде

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

практически любой бэкенд рано или поздно захочет распарсить CSV, например. Опросить три сторонних сервиса и собрать результаты

Ну с сервисами это еще как-то натягивается, хотя я видел в проде системы, делающие это однопоточно - форк-то дёшев. Но уж CSV-то к асинхронщине коим боком? Классический императивный парсер, если забыть о вариабельносьти и полной говнистости формата, то и условный lex/yacc построил бы дерево автоматом вполне императивно.

А я играю с шести лет, кандидат в мастера спорта, выиграл два международных турнира. Мне нравится.

Что касается CSV, как только клиент вам начнет присылать гигабайтные CSV, вам захочется их парсить всеми ядрами, там линейный прирост.

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

Только если порядок строк не имеет значения.

То что люди думают, что факт наличия геттеров и сеттеров означает, что у них теперь инкапсуляция - не вина ооп

Верно. Но ООП и не помогает с инкапсуляцией в смысле потокобезопасности. Скорее, мешает.

А мешает, простите, как?

Вот есть поле, поле вы никак защитить не можете. Плохо.

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

Теперь мы в геттер и сеттер вкорячили мьютекс. Теперь видно, чем лучше стало. А не было бы геттера/сеттера - было бы все еще плохо.

Т.е., получается, помогает? Ну, исходя из элементарной логики!

Почему это я поле защитить не могу? Еще как могу. Даже в джаве.

И как вы собираетесь, не используя инкапсуляцию, защитить поле? synchronized? Ну так:

  1. Работает либо с методами, либо с блоками кода - т.е. поверх инкапсуляции.

  2. Собственно по сути является инкапсуляцией.

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

помогает

конечно, помогает

но для сложных приложений так будет много ошибок

Вот это ваша цитата, ответ на которую вы сейчас цитируете.

Но ООП и не помогает с инкапсуляцией в смысле потокобезопасности. Скорее, мешает.

Теперь таки "конечно, помогает".

Вы там определитесь уже, что ли. Так помогает или мешает? Люди волнуются!

даже плохой инструмент помогает

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

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

Но это не значит что ООП не применимо. Включите голову, применяйте.

Мне? Ничего не мешает. Там в тексте есть и о том, почему блокировка — костыль, а не решение.

С возвращением! Наконец-то можно снова читать ваши срачи с нин-жином

Несмотря на то, что сам я ушел из большого ООП¹  ...

¹ Для занудных буквогрызов: я использую термин ООП не в первородном смысле, в котором его первым употребил Алан Кай, а в том, которое повсеместно распространилось сейчас с легкой руки Гослинга — наследование, инкапсуляция, классы.

Наверное, это случилось потому, что концепция классов/объектов была искусственно привнесена в языки программирования, в которых изначально ничего этого не было. В результате, произошла некоторая подмена понятий. И, вместо того, чтобы реализовывать изначальную концепцию, сделали то, что было всего проще. Это что-то вроде поиска под фонарём.

Кстати, о чём там говорил Алан Кей? Об обмене сообщениями?

... лично мне быстрее, проще и понятнее — реализовывать свои проекты на функциональном эликсире?

Скажите, пожалуйста. Что нужно делать, если кто-то заинтересуется этим самым Элисиром? Как его можно/нужно попробовать? Не означат ли всё это, что в Элисире нет проблемы, о которой Ваша статья?

И вот, наконец, меня озарило. Объектная модель всем хороша в однопоточной среде.

Тут, с очевидностью, возникает два вопроса.

Вопрос первый. Кажется, что многопоточность требует своих специальных средств, а у каждой модели (включая, очевидно, и ООП) есть свои естественные ограничения. Наверное, это должны понимать все специалисты.

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

Есть ещё и третий вопрос. А как можно попробовать многопоточность? Чтобы самому поэкспериментировать и самому почувствовать.

практическое программирование это сплошная подмена понятий и срач в результате

Пардон, я каким-то образом пропустил этот комментарий.

① у акторной модели нет никаких ограничений, о чём есть упоминания и в самом тексте, и в половине моих комментариев
② теоретически, это так; практически — мне известны все без исключения существующие на сегодняшний день парадигмы (в ООП обычно используют саги для поддержания eventual consistency)
③ в каком смысле, как? найдите библиотеку, реализующую многопоточность (или в идеале — акторную модель) для языка, в котором вы себя чувствуете наиболее уверенно — и посмотрите на примеры, а еще лучше — на исходный код

Забавно, что во всех примерах по многопоточности с помощью мьютексов и прочих condition variable, учат не распараллеливать задачи, а наоборот, приводить задачи из всех потоков в один. Что потом и делают ученики.

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

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

Смотря как им пользоваться: я как-то завалил собес, используя мьютекс и condition_variable для каждого экземпляра потока, чтобы сделать блокировку двусторонней: любой из потоков может запретить главному пользоваться ФС, также главный поток может запретить начинать генерацию данных всем потокам до тех пор, пока текущие данные не будут сохранены. Но потенциального работодателя такой креатив не устроил. Судя по критике, ему нужно было именно бутылочное горлышко. А вопроса про смысл многопоточности, похоже, вообще не услышал.
P.S. опережая вопрос про дедлок, он был исключён, так как wait с одной стороны физически не мог начаться во время wait с другой. Это было предусмотрено.

опережая вопрос про дедлок […]

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

Про решение можно (и нужно) долго разговаривать отдельно: навскидку его довольно непросто сопровождать, если появятся дополнительные причины блокировок, но как стартовая точка для предметного разговора по душам (после того, как собес сам по себе завершен с положительным исходом) — отлично.

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

Я знаю достаточное количество контор, где очень ценят умение выразить своё мнение вместо «приподзаткнуться» :)

Закончу текущую задумку и снова начну поиски. Надеюсь, именно такая контора мне и попадётся. Сложно работать в среде, где ты "инопланетянин" 👽))

Например? По моему опыту, таких сильно меньшинство.

Их мало, но достаточно.

Огласите весь список (с)

По моему опыту — каждый третий, если не второй, европейский стартап.

А, ну так в стартапах СВЯО АТМСФРЕА (с). Я по выжившим смотрю...

Разная атмосфера, как везде.

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

А есть ли смысл распараллеливания в таких случаях?

Однопоточная часть занимает небольшую долю времени от общего времени операции. Я это и имел в виду.

Конечно, имеет. Можно вычислить несколько ускорится по формуле / закону Амадала

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

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

Один стейт — один поток. Только тут, но это даже специально.

Очередная статья на тему какой плохой ооп.

Ответ на вопрос статьи - что не так с ООП в 2025: с ооп все так. Пользуйтесь ооп на здоровье и в свое удовольствие и не слушайте никого.

Статью можно не читать даже

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

Проблемы дешевле решить добавив нового железа и взяв ещё десяток тупых и дешёвых тушканчиков из "Сова эффективный менеджер"

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

А где он, тот "экскаватор"? Назовите тогда уж, а то непонятно. Не похоже, что бы языки, подчёркнуто игнорирующие ООП, этим самым "экскаватором" оказались. По популярности они очень сильно далеко от топа с ООП языками... Выходит, все дураки, лопатами копать продолдаютЪ 😁

Если вам ооп доставляет боль, почему вы не перейдете на го, раст и другую строгую функциональщину?

Го имеет слабые возможности ФП. Для гарантий сложных правил бизнес логики не пригоден, только для простых сервисов request response.

Rust неплохая штука, тренируюсь. Но простые сервисы писать на нём это как гвозди забивать микроскопом

Какую бизнес логику не покрывает язык?

Для гарантий сложных правил бизнес логики не пригоден, только для простых сервисов request response.

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

Любую логику покрывает любой полный язык. Вопрос не в этом, а в том, помогает он, или мешает.

гарантии инвариантов, например

С помощью enum можно сделать некорректные состояния непредставимыми на уровне типов.

Пример: Объект Соединение может быть enum Connection { Disconnected, Connecting(ip), Connected(socket) }.

Нельзя случайно вызвать read() у Disconnected соединения — такого метода для этого варианта enum просто не будет.
Код не скомпилируется. В Go это будет объект с полем state и кучей if-ов, которые легко забыть. Опять же, на простом примере этого не видно. Погрузитесь в сложный бизнес процесс и поймете.

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

нет, конечно. Реализации будут разными и в некоторых языках вы просто по статистике статистически будете делать больше ошибок.

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

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

Rust, ADT

Я уже привык к такому синтаксису и пишу просто при проектировании

Много таких: Haskell, OCaml, F#, Scala, Rust... На самом деле так в любом можно, но не так удобно, просто

Из всего этого великолепия для запрошенного подходит только OCaml, да и то со скрипом.

Так, а как они будут это делать, если состояние конечного автомата меняется в рантайме? Если чисто рантаймовые проверки, так у меня и на Perl почти готовое решение будет.

Языка у меня пока нет, но библиотеку такую я уже написал: https://hexdocs.pm/finitomata/readme.html

Синтаксис описания — Mermaid/PlantUML, компилятор делает все проверки (пока — почти все, со сложными ветвлениями руки не доходят разобраться, пока просто ворнинги в LSP редактора про то, что вот тут наворочено, перепроверьте).

Ммм... Описание на PlantUML выглядит, конечно, завлекательно - но там, похоже, поддерживается только один язык программирования? Изучить Erlang у меня так ни разу руки и не дошли (по всё той же причине - узок их круг, работу на нём не найти)... но всё тот же вопрос с рантаймом остается: если мы заранее не знаем, в каком стейте сейчас автомат, т.к. это зависит от входящих извне сигналов (например по сети) - то как выполняется проверка "вот этот метод (сигнал) сейчас вызывать недопустимо" ?

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

В эрланге есть паттерн-матчинг в заголовках функций, транзицию из недопустимого стейта он не даст скомпилировать.

"Личный опыт" вместо статистики и называется "узок их круг".

Пример матчинга есть? Я подозреваю, мы о разных проблемах говорим.

Вечером подробнее отпишусь, я этот кусок все равно давно в документацию хотел добавить.

Об одном, об одном мы говорим.

Модуль стейт-машины выглядит примерно так (это эликсир, но синтаксис довольно очевидный, мне кажется).

defmodule MyFSM do
  @fsm """
  idle --> |accept| accepted
  idle --> |reject| rejected
  rejected --> |accept| accepted
  """

  # эта директива на этапе компиляции
  #   добавит всякого полезного как раз
  use Finitomata, fsm: @fsm, auto_terminate: true

  @impl Finitomata
  # это колбэк, который вызовет процесс актора
  def on_transition(:idle, :reject, _event_payload, state_payload) do
    # тут можно что-то сделать
    {:ok, :rejected, state_payload}
  end

  # это он же, для других параметров
  def on_transition(state, :accept, _event_payload, state_payload) when state in [:idle, :rejected] do
    # тут можно что-то сделать
    {:ok, :accepted, state_payload}
  end
end

Компилятор эликсира умеет выполнять код на эликсире прямо в процессе компиляции, поэтому он:

① проверит консистентность FSM (одно начальное состояние, минимум одно конечное, нет сирот, …)
② сгенерирует заглушки для нереализованных явно однозначно разрешимых переходов (например, event :reject — однозначно определяет target state, ему не нужен специальный обработчик, если не требуются сайд-эффекты, поэтому если clause on_transition(_, :reject, _, _) эксплицитно не реализован, будет вставлена заглушка, которая ничего не делает и просто переводит автомат в состояние :rejected
③ упадет с ошибкой, если есть нереализованные явно переходы с несколькими возможными конечными состояниями
④ магически добавит handle-all clause, который будет обрабатывать ошибки
⑤ документацию еще нагенерит, с картинкой и прочим, вот как тут или тут — обе диаграммы сгенерированы компилятором

В результате, если будет вызван разрешенный переход, то его разрулит соответствующая голова функции on_transition/4. Если запрещенный — состояние не изменится, в стейте сохранится ошибка, в лог и телеметрию — упадёт ворнинг.

Это обертка над обычным актором, которая принимает все сообщения и вызывает соответствующие колбэки (on_transition/4). Инициировать переход при этом, очевидно, можно только ивентом:

Finitomata.transition(fsm_name, {event, payload}, delay \\ 0)

Больше вообще никак доступ к состоянию не получить (на чтение можно, ладно :).

эликсир, но синтаксис довольно очевидный

Не особо. Что значат вот эти двоеточия перед bareword?
{:ok, :rejected, state_payload} - это что? Похоже на JSON.

Потом, тут же еще обертка модуля, так что - это автомат Мили или автомат Мура? То есть, если рисовать графом, в каких местах будет выполняться код - узлах или ребрах? Зачем нужен колбэк, разве код не выполняется интуитивным образом, когда сигнал = метод, если в терминах ООП?

Что значит слэш четыре в упоминаемом названии метода?

Это обертка над обычным актором

Это ж надо было видеть "обычные акторы" еще...

Инициировать переход при этом, очевидно, можно только ивентом:
Finitomata.transition(fsm_name, {event, payload}, delay \ 0)

Совсем не очевидно... помнится, когда мы в Telegram-клиенте на Perl с товарищем автомат влепляли, первая версия делала вызов на стейт чисто для простоты, с коллбэками на выход из стейта и на вход в новый. Планировалось переделать это на более удобный FSM:

sub start_session
{
    my $self = shift;
    return $self->_state('session_ok') if defined $self->{session}{auth_key};
    $self->_state('phase_one');
    AE::log debug => "starting new session\n" if $self->{debug};

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

Я себе сделал мини DSL для автоматов:

receive = runState state
    [ readyToReceive     |-> receivePreamble
    , receivingMessage   |-> receiveMessage
    , receivingConfirm   |-> receiveConfirm
    , receivingDiscovery |-> receiveDiscovery
    , receivingPing      |-> receivePing
    , skippingAll        |-> skipAll
    , skippingMsg        |-> skipMsg
    ]
receivePreamble = do
    runInput
        [ discovery rxPreamble |-> start receivingDiscovery waitingMac
        , ping      rxPreamble |-> start receivingPing      waitingAddress
        , confirm   rxPreamble |-> start receivingConfirm   waitingAddress
        , message   rxPreamble |-> start receivingMessage   waitingAddress
        , discovery txPreamble |-> start skippingAll        11
        , ping      txPreamble |-> start skippingAll        3
        , confirm   txPreamble |-> start skippingAll        3
        , message   txPreamble |-> start skippingMsg        2
        ]
receiveDiscovery = runState phase
    [ waitingMac     |-> receiveDiscoveryMac
    , waitingAddress |-> receiveDiscoveryAddress
    , waitingMsbCRC  |-> receiveMsbCRC
    , waitingLsbCRC  |-> receiveDiscoveryLsbCRC
    ]
receiveMessage = runState phase
    [ waitingAddress   |-> receiveAddress waitingTid
    , waitingTid       |-> receiveMessageTid
    , waitingSize      |-> receiveMessageSize
    , waitingData      |-> receiveMessageData
    , waitingMsbCRC    |-> receiveMsbCRC
    , waitingLsbCRC    |-> receiveMessageLsbCRC
    ]

Тут фрагмент потокового парсинга бинарного протокола приведен

Страшно даже себе представить, сколько придется переписать, если в протокол добавится новое поле.

Синтаксис незнаком, вот во втором примере когда после стрелочки несколько слов - они что значит?

Параметры функции, тут нет скобочек

Что значат вот эти двоеточия перед bareword?{:ok, :rejected, state_payload} - это что?

Атомы. Как строки, но переиспользуют память.

автомат Мили или автомат Мура?

Мили, конечно.

Зачем нужен колбэк

Чтобы скрыть состояние (и убрать под капот бойлерплейт). За этим кодом — актор. Finitomata.transition/4 ему отправит сообщение. Он выполнит все проверки и дёрнет колбэк. В котором всё, что пользователю дозволено — переход, или отказ (и сайд-эффекты).

Что значит слэш четыре в упоминаемом названии метода?

Арность, количество принимаемых аргументов. Это традиционная запись для эрланга, потому что foo() и foo(42) — это две разные функции с точки зрения компилятора.

Совсем не очевидно...

Очевидно не в смысле из кода, а в смысле из постановки задачи. Пускать шаловливыми ручонками напрямую к стейту — варварство, которое разрушает математические гарантии.

вызов на стейт чисто для простоты

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

Спрашивайте, конечно, всегда рад.

Я давно перестал пить коньяк по утрам.

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

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

Раст не решает проблемы с рейс кондишинами, см. кусок про дедлоки. Раст очень сильно переоценён.

Ну тогда большинство языков сильно переоценено. Как попадается задачка со звёздочкой понимаешь что язык ну совсем не помогает в её решении.

Мейнстрим языки - бери больше, кидай дальше

Не совсем. Почему-то излишней рекламе/хайпу подвергаются только некоторые языки, а не большинство. Я так навскидку и не припомню даже других, кроме Java, Go и Rust. Ну, Питон so-so, но он свою популярность на хайпе от смежных тем получил (ML например), а не активным непосредственным пиаром.

Раст очень хотел в ядро Линукс. Но пока не вышло. Вместо этого он мог бы развивать другую сильную сторону: автоматизацию сложных бизнес процессов и многопоточность. Но... Не вижу движения туда.

Хотя, я не назвал бы его сложнее Scala или Elixir

Эликсир в сто раз проще.

Джава и го — это чистый too big to fail по создателю. Гуглу хорошо — и нам значит подойдёт.

Раст — загадка. Особенно, учитывая, что создатели не осилили на нем написать проект, ради которого он создавался.

Там смысл имелся не в том виде, кто создатель, а в том, что целенаправленно вкладывались бабки на пиар и прочий маркетинг. Так-то Microsoft тоже сделал не только язык, но аж целое семейство, и он тоже too big to fail.

Раст — загадка. Особенно, учитывая, что создатели не осилили на нем написать проект, ради которого он создавался.

У меня на эту загадку есть ответ, но он, кхм, астрологический :) Технических причин в его хайпе разумеется нет.

Ну тогда может быть вам пхп подойдет, там этой проблемы нет как таковой и без ооп можно обойтись :)

С чего вы решили, что хотя бы знаете по имени языки, которые мне подходят?

Как можно противопостовлять ООП и многопоточность... Вы вообще не понимаете о чем говорите, что за большое ваше ООП тоже не понятно (из которого вы ушли).

Или вы не понимаете, о чем говорю я. Так тоже бывает. Например, я нигде ничего никому не противопоставлял.

Вы просто, опираясь на то, что ФП дает гарантии защиты в многопоточной среде (ввиду отсутствия разделяемого состояния), считаете, что ООП или процедурная парадигма должны давать таковые же.

Ну вот не должны. Потому что ООП - парадигма, построенная на наличии разделяемого состояния, со своими ограничениями и областью применения. Вся фишка в том, что в 90% случаев именно ФПшные гарантии вообще не нужны, а ограничения накладывают.

Аргументы про "у меня много ядер, займите все" - ну, такие себе. Есть десктоп, на котором эта утилизация нафиг не нужна. Есть кейсы (особенно в банкинге, пример из которого вы привели для иллюстрации), когда необходима именно гарантия последовательного исполнения операций - чем тут ФП поможет?

Код на 90% синхронный, и да, в современном серверном применении - тоже. Многопоточные гарантии "искаропки" хорошо, когда вычислительно-сложные задачи выполняешь. Их, как правило, в процессинге пользовательских запросов нет.

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

Вы просто, опираясь на то, что ФП дает гарантии защиты в многопоточной среде (ввиду отсутствия разделяемого состояния), считаете, что ООП или процедурная парадигма должны давать таковые же.

Ну вот не должны.

Половина присутствующих считают что инкапсуляция включает потокобезопасность, а половина что нет. Да что же с ООП не так?!

Ну т.е. половина в своих суждениях опирается на определение ООП и какую-то литературу по ООП-парадигме, а вторая - открывает любой текст, но видит перед собой ФП-методичку.

То, что ФП в качестве побочного эффекта дает потокобезопасность, совершенно не значит, что ООП должно давать те же либо сходные гарантии. Список гарантий ООП нужно искать в литературе, посвященной ООП, а не в ФП-откровениях.

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

ФП: у нас нет разделяемого состояния, можете не заботиться о потокобезопасности (пока не понадобится shared state/shared resourse, но с ними из без потокобезопасности больно было).

ООП: вот вам инструмент, вы его знаете, он с вами давно, называется "инкапсуляция". А вот новые, называются "примитивы синхронизации". Защитите себя сами, вы на это и программисты.

С хрена ли тут какие-то гарантии дополнительные у ООП появиться должны были - решительно непонятно.

убедили, ООП плохой инструмент, пользоваться им не стоит

Не пользуйтесь, разрешаю

Я, откровенно говоря, C++-style ООП тоже не пользуюсь, процедурным языком кормлюсь

Сначала появилось ФП, потом процедурная парадигма (плавно переходящая в структурную), потом ООП, и только потом - многопоточные системы.

Хватит уже чушь нести.

Сначала появилось ФП, потом процедурная парадигма (плавно переходящая в структурную), потом ООП, и только потом - многопоточные системы

Да ну неправда. Первым был императивный Фортран, и многопоточностью занялись уже в 70-е, если не 60-е. Даже современные принципы виртуализации были сформулированы и у IBM уже работало в 70-х.

Я не приводил пример из банковской сферы. Я не опирался на то, что ФП дает какие-то гарантии (не дает). Я вообще не про ФП.

Ну пример 5000+ параллельных юзеров норм хайлоад? Проект на c# микросервисы одна бд. Все сделано на ооп и dependency inj. Работает норм. Есть и собственные workflow в коде. Нет никаких проблем с ооп. Проблемы были только с теми методами или запросами к бд которые выполняются долго. Это приводит к перегрузке очереди запросов. Так что главная вещь это оптимизация.

микросервисы это почти наверняка простые запросы, без сложных связей

300+ таблиц в бд. В каждой табличке по 2-10 связей норм?) Про бизнес логику не говорю) это медицинская система

случаем, не система учета несчастных случаев? ФГИС СОУТ

написана на C#, неплохо, но это не highload

я ее код ревьюил на предмет уязвимостей

медицинских больших систем в России кот наплакал и я их почти все знаю

ЕВИАС, ЕМИАС, ФГИС СОУТ

из них код не читал только ЕМИАС, но примерно представляю

А я где-то говорил, что это невозможно?

Можно и микроскопом шуруп заколотить, было бы усердие.

Что не так с ООП в высокосвязном хайлоаде

Все таки, есть способы привести высокосвязный процесс и данные к низкосвязным

Например:

  • исключить кольцевые зависимости

  • N-связность привести к нескольким 2-связностям, пожертвовав ACID

  • и так делее...

Но чем сложнее система тем такие способы изоляции хуже работают

Так тут проблема не ООП, а в управлении состоянием, например на Rust это надо очень сильно извратиться чтобы добиться проблем типа гонки данных на мьютексе, прям осознанно и больно извратиться, а пример с 16 потоками решит любой, кто досчитал до тем Arc/Rc, Mutex вообще не встретив какого-то сопротивления

А раст у нас что, представляет классическую традиционную ООП-парадигму? В расте у вас будут бругие проблемы, когда захочется запустить 1000 и больше потоков (так бывает), вы столкнетесь с дедлоками в самых неожиданных местах. Но всяко проще, чем на классическом ООП.

Я даже маленько опешил от такого наезда...

1) что такое классическое ООП? Автор концепции ООП сказал что C++/Java это вообще не то что он имел в виду и пошёл пилить Smalltalk

2) дедлоки это архитектурная проблема, а не проблема языка, и на чистых функциях хаскеля вы можете добиться дедлока, что показано и через машину Тьюринга и через исчисление Чёрча

3) Еесли мы говорим про обычный компьютер, то 1000 потоков это антипаттерн, у вас на переключение задач в ОС уйдет больше времени, чем на саму задачу, плюс под каждый поток по 2 метра отрезать? Не жирно, есть корутины, есть лёгкие потоки...

Вообще хочется конкретного кода, в чём там у вас проблема

⓪ какого еще наезда? почему наезда?

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

② язык легко может от них застраховать, в 99% случаев

③ я про гринтреды, конечно

④ у меня вообще нет никаких проблем

дедлоки это архитектурная проблема, а не проблема языка

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

2 метра виртуальной памяти ничего не стоят, это просто ограничение сверху на глубину стека.

А что там за проблемы у них?

Да всё те же: мьютексы вместо мэйлбоксов. При высокой связности такая модель неизбежно рано или поздно приводит к циклической зависимости, а там дальше либо есть гений, который это разруливает каждый раз руками, либо (как и случилось с проектом, ради которого авторы вообще придумали раст) — «да что за день сегодня! вообще ничего не получается!».

А это реальный опыт или умозрительно? А то мне любители Раста на Хабре не один раз высокомерно доказывали, что это я отсталый и не хочу его учить, а там на самом деле компилятор сам всё синхронизирует, и кривой код с гонками просто не скомпилится.

либо (как и случилось с проектом, ради которого авторы вообще придумали раст) — «да что за день сегодня! вообще ничего не получается!».

Кстати, а что именно там случилось? А то так-то в Firefox код на Rust есть, я несколько месяцев назад чертыхался, когда после апгрейда с Tree Style Tabs он сломался нахер, что половина интерфейса не работала, и просто так собрать в бисекте предыдущую версию не выходило - там модули компилировались растом 1.78, но перестали собираться более свежим системным растом 1.80 (такое, кстати, тоже очень серьезный симптом отстойности языка).

Да откуда дедлокам то взяться? Ресурс один - блокировщик один на чтение и запись, пока не завершится работа с данными. Если нужно, можно сделать парадлельное чтение, но на запись нужен второй блокировщик, который и чтение запретит. И это уже реализовано в БД и не связано с ООП вообще ни как

Что значит «ресурс один»? Ресурсов много, и среди них рано или поздно появится циклическая зависимость.

Отсылки к акторной модели и всему такому особенно смешны в контексте того, что она и есть классическое ООП, ибо Alan Kay создавал вовсе не С++ с Java. Так что вместе с, например. вот таким:

Среда, которая буквально приглашает разработчика ошибиться и разрушить тотальность функций потенциальным дедлоком — не должна иметь права на существование в принципе.

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

И да, шо такое "тотальность функций"?

шо такое "тотальность функций"?

определенность, вычислимость на всей заявленной области допустимых значений

причем, вычислимость за конечное время )

А, то есть опять read(2) и printf(3) мимо? =)

Я триста раз оговорился же, что использую определение, прижившееся после Гослинга, а не оригинальное Аланкаевское. Иначе я бы еще триста минусов отхватил за незнание того, что такое ООП, плавали, знаем.

Вот про тотальность функций: https://docs.idris-lang.org/en/latest/tutorial/theorems.html#totality-checking

Блин, там опять требуется знать какой-либо функциональный язык... а простыми словами можно?

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

А, то есть это, что пытались сделать в безопасных системах типа BPF, например eBPF-верификаторе для обеспечения безопасности ядра от юзерского кода (получилось, конечно, отвратительно).

Угу. Ну вот в идрисе получилось неплохо, но он не для продакшена.

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

Извините за оффтоп, но трудно молчать. Я стал так часто полагаться на ИИ в вопросах выбора, что такие статьи ввергают в уныние. Словно мы с вами в маленьком клубе радиолюбителей: ведём философские беседы, когда ИИ сделает как-нибудь, и оно будет работать достаточно хорошо.

А потом всё быстро угаснет и останется в прошлом.

Вот и выросло поколение, которому нечему научить даже тупого ИИ.

Почему же? Я в программировании лет 15. Мне просто надоело чистить картошку ногтем. Продукт сделать - вместо месяца труда - неделя. Мне не очень важно, как это сделано. Оно работает достаточно хорошо.

Я решаю задачи, которые ИИ и близко решить не в состоянии (и не сможет, пока вместо LLM не придумают адекватный подход).

Я честно пробую каждый месяц или типа того, но «улучшить документацию» — это единственное, что можно хоть как-то передоверить LLM, остальное, даже тесты, — для моих задач — приводит к лютому бреду.

Придумал пример для сложной реализации потокобезопасного решения и при этом быстрого. .

Движок электронных таблиц типа Google sheets. Я такое делал на практике на epplus. Там уровней блокировки очень много

  • На весь файл

  • На лист (формулы между листами редко и можно eventual consistency потому что пользователь не видит одновременно несколько листов, если только не придумает открыть 2 браузера рядом. Да и внутри Excel файла каждый лист это отдельная XML

  • На набор ячеек с формулами, которые ссылаются на изменившуюся ячейку

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

    Скорость, конечно же так себе, тысяча операций в секунду на файлах 100 столбцов*10000 строк*10 вкладок.

    Чужая ООП библиотека, особенно не кастомизировать...

Да, это любопытная задача.

При чем тут ООП я вообще не понял...

Вы жалуетесь, или хвастаетесь?

Объектная модель всем хороша в однопоточной среде.

Что? На C#, Java, C++ например ООП нормально работает в многопоточности. Честно, дальше не читал, но судя по комментариям, там перлов много. Уже молчу, что по логике автора, те же проблемы могут возникать вполне себе и при ФП. Потому что конкурентоспособность никто не отменял и изменение одного и того же объекта может вызвать такие же проблемы.

Вообще, проблема многопоточности имеется в основном на web бэкенде. Изначально подразумевалось, что потоки должны быть изолированы и потом уже добавили возможность получать или изменять данные из одного потока в другом при помощи всяких invoke. Однако, на серверах может крутиться множество потоков, либо, как с node js, один основной поток и очереди на выполнение в нём кода. При этом все эти потоки по сути выполняют один и тот же код, с одними и теми же данными, что и является проблемой организации конкурентного доступа, а не парадигмы. В других многопоточных приложениях, обычно не такое большое количество потоков и важнее синхронизация данных между ними. Например, игра в которой game loop это один поток с рендером и логикой, звуки про омываются во втором потоке, ui например это третий поток. Пример стрёмный, но в контексте хороший. В такой игре мы имеем изолированные потоки, выполняющие разный код с разной бизнес логикой, их всего 3 и важна синхронизация данных между ними. А на бэкенде код и бизнес логика одна порой на сотни потоков, что приводит к другим проблемам.

Я не работаю с вебом и не противопославляю ФП — ООП.

что же все-таки не так с ООП, если лично мне быстрее, проще и понятнее — реализовывать свои проекты на функциональном эликсире

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

Иными словами, в ООП нет ничего прям плохого, но только пока вы не погрузились в сильно связанный хайлоад.

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

термин хайлоад, чаще всего применяется в контексте работы с веб бэкендом

Чё?

не слово о проблемах с аллокациями памяти и сборщиками мусора, работой с общими состояниями, тот же спагетти код с монадами в сложных проектах

У меня нет проблем с аллокациями памяти, тем более — со сборщиками мусора. Общих состояний нет в принципе, общее состояние — это ужасный антипаттерн. ПРо проблемы с монадами слышу впервые.

Ох... вот и выросло поколение (с)
Чтоб понятнее было т.е. далеко не уходить: вот, например, система защиты от DDoS-атак или файрвол на 100 Gbps - вот это хайлоад. Система обработки карточных платежей в онлайне (терминалы и банкоматы) - вот это хайлоад. Нигде там веб даже и близко не стоит.

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

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

Вообще, проблема многопоточности имеется в основном на web бэкенде.

Лолшто? Кого интересует этот веб, где традиционно вообще пускали по процессу на пользователя? Многопоточность используется в действительно серьезных сферах.

А вариант примера решения без ооп будет? И как объект "диспетчер" оказался в управляемых объектах? И ооп в этом примере за уши притянут. Согласен, проблемы есть, как и есть способы их решений, но пример на псевдокоде - это вообще не ооп ни разу, обычное процедурное программирование.

Примера решения чего? У меня практически в каждом тексте кроме этого есть ссылка на мои библиотеки, которые решают те, или иные задачи.

Что такое «объект „диспетчер“», какие еще «управляемые объекты»? Вы о чем вообще?

Пример на псевдокоде — строго про инкапсуляцию, как там и написано.

Там не совсем инкапсуляция, и к ООП он не относится, ну да ладно. Я имею в виду пример с самолётами, он слишком натянутый, при нормальной архитектуре в ООП такого не должно случаться, если уж зависимость так необходима. Да, дедлоки случаются, обычно при добавлении функционала, которого изначально не было предусмотрено, но это лишь причина пересмотреть архитектуру приложения. При работе с бд в примере, вам нужно создать объект типа похожего по функционалу на lockguard для блокировки изменения данных строки в БД. Просто ещё один класс. И он уже будет освобождать строку был в деструкторе. Это один из вариантов решения.

Я имею в виду пример с самолётами, он слишком натянутый,

Пример с самолётом нам принесла жизнь, простим уж старушку-природу.

при нормальной архитектуре в ООП такого не должно случаться, если уж зависимость так необходима

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

Автор конечно же мастер провоцировать срачи в комментариях, моё уважение. Ну и очень интересно - так а что у вас за задача на 16 потоков? Если что, высоконагруженную многопользовательскую систему тоже писал, а даже собственные примитивы синхронизации. Разумеется, в парадигме ООП)

У меня типичная задача не на 16, а на 16К (и больше) — ну не потоков, гринтредов, эрланговских процессов, но сути тут это не меняет.

Прилетают из стороннего сервиса со скоростью 60К/сек через раббит обновления примерно 20К разных сущностей. Они зависят друг от друга, их нужно обрабатывать, хранить историю за последний час и всякое такое.

Тут всё же надо сделать уточнение - 20К разных сущностей или экземпляров этих сущностей? Ну например если у меня в системе есть читающие и пишущие потоки в разделяемую бд - то это всего лишь 3 сущности вне зависимости от общего количества этих потоков.

Зависимых друг от друга экземпляров. Пример из реальной жизни: курсы валют из стороннего источника. В принципе, сам по себе курс — почти изолированная сущность, но его надо проверять и/или обновлять в зависимости от соседей: вот пришел курс для экзотической пары Kenyan Shilling (KES) / South African Rand (ZAR).

Такие пары напрямую торгуют крайне редко, поэтому надо удостовериться, что там не фантазия провайдера на тему фазы луны в меркурии. То есть, взять KES/USD, USD/ZAR, перемножить их и сравнить с пришедшим. А если KES/ZAR не приходит уже полчаса — так тоже бывает — то напрямую посчитать через мажорые пары.

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

Разумеется, в парадигме ООП

Я же не говорил нигде, что это невозможно. Можно, конечно. Просто ну вот сразу: вам пришлось заморачиваться примитивами синхронизации, а у меня гарантии целостности и отсутствия гонок из коробки. Что удобнее?

Ну лично мне удобнее самому контролировать нюнсы многопоточной синхронизации, а собственные примитивы синхронизации и писались именно для удобства. Решения из коробки не всегда лучше и предпочтительнее кастомизированных. Я например не использую async/await в c# и даже считаю их использование плохой практикой.

Вот хорошее чтиво перед сном: https://getakka.net/

async/await — это костыли, конечно, тут спору нет.

А что не костыли / удобнее этих промисов, если для более-менее массового языка? Гринтреды с ручной кооперацией (yield) ?

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

Тут "акторная модель" подразумевает какой-нибудь Erlang, а я спросил для более распространенных языков, как можно было бы сделать. Уточняю, с точки зрения пользователя (программиста-потребителя), а не автора среды.

По ссылке три комментария выше сходите. Акторную модель принесли и в джаву и в шарпы уже давно.

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

я спросил для более распространенных языков

:)

Я ответил для более распространенных. Боюсь, что на близком к железу уровне — всё уже придумано до нас. Я в последний раз возился с ревёрс-инжинирингом одной видеокарты в 1995 году, с тех пор — всё дальше и дальше от железа :(

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

Не, в этом треде скорее про Питон или JS, что было бы удобнее на них вместо async/await, про сетевое железо в другом треде. Хотя я оба почти не знаю, мне был бы ближе Perl... к которому есть Coro для кооперативной многозадачности, т.е. надо явный cede (yield) делать. Серьезно его еще не пробовал, поэтому не могу сравнить.

На JS — ничего, там вся архитектура сопротивляется параллельности: асинхронность дали, и на том спасибо.

На питоне — просто не знаю.

На Coro я глянул: выглядит вроде достойно.

Почитал про Akka и не нашёл ничего того, что уже не делал сам. Вместо хэлловорда в 10х кода было бы интереснее увидеть пример, который самостоятельно сделать очень сложно, а с аккой очень просто. А по примерам сам фреймворк построен поверх ООП, поэтому противопоставлять их тем более странно.

Вот например. Посылка сообщений предполагает иметь очередь для их хранения. Где хранится эта очередь? Что будет, если актор не будет справляться с их обработкой и очередь начнёт бесконтрольно расти? Сможет ли восстановить их после внезапной перезагрузки? Или предполагается, что он будет пересылать их на другой узел другому актору? А организацию распределённого хранения данных в таком случае он тоже на себя берёт или как? Вот на такие очевидные вопросы в первую очередь хочется получить ответ.

Я не автор Akka, я лишь разместил объяву.

сам фреймворк построен поверх ООП

А как вы предлагаете строить фреймворк в джаве/шарпе? На ассемблерных вставках?

Посылка сообщений предполагает иметь очередь для их хранения. Где хранится эта очередь?

Для Akka — не знаю, предполагаю, что в размазанном по кластеру глобальном состоянии.

Что будет, если актор не будет справляться с их обработкой и очередь начнёт бесконтрольно расти?

Это проблема (кажущаяся) всех без исключения реализаций акторной модели. Мейлбокс переполнится и актор взорвётся. Если подумать, это не проблема, потому что такую ситуацию все равно допускать нельзя по очень многим причинам. Любая простейшая метрика поможет её предотвратить.

Сможет ли восстановить их после внезапной перезагрузки?

Без клиентского кода — нет (скорее всего нет, но Akka могла и заморочиться). Но написать свой код, который это сделает — не так сложно.

Или предполагается, что он будет пересылать их на другой узел другому актору?

Как вариант, но тут придётся воевать с rolling updates.

А организацию распределённого хранения данных в таком случае он тоже на себя берёт или как?

Конечно. У актора есть стейт, а на какой ноде он выполняется — клиентскому коду фиолетово.

Это проблема (кажущаяся) всех без исключения реализаций акторной модели. Мейлбокс переполнится и актор взорвётся.

Ага, не зря значит подозревал. И не зря значит свою очередь писал с кэшированием на диск. И конечно не от нечего делать, а когда с этой (кажующейся) проблемой столкнулся лично.

Но написать свой код, который это сделает — не так сложно.

Так и CreateThread с минимальной обвязкой написать несложно, только теорию надо чуть дальше мьютексов изучить.

Это неправильное решение. Лечить back pressure кэшированием на диск в акторной модели — значит добровольно отказаться от всех её плюсов.

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

Конечно нет. Гарантия доставки — это костыль.

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

А как оно предполагается кодить без гарантий доставки? Для сетевого инженера терять гарантию доставки выше L4 звучит как-то странно.

Для сетевого инженера достаточно гарантии доставки минимум один раз, а в программировании в огромном множестве ситуаций (неидемпотентные вызовы) — требуется ровно один раз. Такую гарантию обеспечить в принципе невозможно (см. задачу про генералов), поэтому люди в каждом конкретном случае строят свой подходящий под условия задачи переговорный процесс. Это может быть синхронный вызов (простейший костыль), консенсус, сага, да много их.

Да почему невозможно, если более-менее на практике, а не 100% случаев теории. Это делает, например MQTT при QoS=2.
Я, кстати, всё никак не доберусь делать телеграм-клиент на MQTT 5, подписка на топики более весёлая штука, чем просто фиксированные мэйлбоксы акторов (по крайней мере, как эти пути показывали в примерах akka .NET)

Потому что у меня более-менее на практике бывают транзакции на $50М, например.

Топики интересны, да, но ничем не отличаются от мэйлбокса, если вдуматься.

Ну вон https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901237 флоу для доставки exactly once.

Топики, разумеется, имели в виду не голые, а с подпиской по маске, типа outer/+/somekeyword или subtopic/# - так можно, например, заставить процесс БД "перехватывать" ответы в reply topic от гуя к воркеру протокола и тоже сохранять эти сообщения в базу, хотя гуй просто просматривал какой-нибудь чатик и не вызывал код для сохранения специально.

Да не работает оно так, как на бумаге, в том и проблема.

Представьте себе ситуацию, когда сообщение принято, полностью отработано, и тут сбойнул жесткий диск и ACK не ушел.

Это пожалуй один из самых интересных вопросов. Как вчера я и допер до этой задачи и думал над ней. И как не крути, в рамках нашей реальности — нужен консорциум, стандарты.

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

И железняки еще должны состоять в этом консорциуме.

На тему срачей в комментариях (я не жалуюсь, мне не 16 лет, но причины настораживают):

Лови политика-пропагандиста!
Лови политика-пропагандиста!

Полагаю, у данного срконструктивного обсуждения два корня:

  1. У понятия «ООП» нет единого строгого логически верного определения, с которым согласны все. Разные участники дискуссии подразумевают разное содержание понятия «ООП»

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

Дык а я про 2) в тексте прямо написал.

Возможно, я ошибаюсь, но из текста вырисовывается несколько иная позиция.

Иными словами, столь тщательно выстроенные годами паттерны, абстракции и вообще краеугольные камни ООП — рушатся при распределении нагрузки по всем процессорам. Инкапсуляции из коробки больше нет.

Иными словами, в ООП нет ничего прям плохого, но только пока вы не погрузились в сильно связанный хайлоад. Вот тогда придется написать заново неспецифицированную, глючную и медленную реализацию половины акторной модели². ООП был современной и крайне удобной парадигмой в девяностые, когда многопоточное связанное программирование было уделом фриков. В 2025 эта парадигма всячески сопротивляется распараллеливанию задач, что при современной доступности количества ядер на единицу выполнения кода — халатность, граничащая с преступлением.

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

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

Мешает не ООП, а мифы, с ним связанные.

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

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

С ООП так же. Хрен с ней, с инкапсуляцией Синглтон ломается. И разработчики к этому не готовы. Не все, как минимум.

Есть и параллельная проблема: ООП поощряет разделяемое состояние. Это я называю прямым сопротивлением многопотоку.

А как бы Вы решали задачу типа файрволинга входящих с сетевух пакетов на wire speed (допустим, от 40 гигабит/с) ? Низкий уровень: скажем 16 тредов, каждый прибит к своему CPU Core, в ядре ОС сидим, что тут можно сделать, когда без общего состояния никуда, например проверки счетчиков в таблице соединений?

Это совсем не моя сфера, но я знаю, куда посмотреть. До компьютера доберусь — отвечу.

Я больше про 100К эрланговских процессов, а не про 16 тредов :) пока навскидку кажется, что нужно буферизовать запись, но могу сильно ошибиться. Вечером, короче.

Не, ну это в условном eBPF/XDP расчет на SMP, в каком-нибудь DPDK/VPP уже будет непрерывный поллинг в воркер-тредах вместо прерываний и поддержка NUMA, так что будет их уже допустим 64. Но всё равно, подход там классический, ибо близко к железу - мьютексы, атомики, местами присыпано немного lock-free типа RCU... А хотелось бы в акторном стиле, хоть сам ерланг туда и не влезет.

Пока вечер наступает, уточнений по задаче подброшу. Предполагается, что мы чистим входящий в датацентр трафик (т.е. полагаем, что размер атаки нам не забил канал), число пакетов в секунду можно прикинуть, поделив эти 40 (или сто) гигабит в секунду на минимальный размер пакета (в пределе это 64 байта, но полагая часть трафика легитимным, можно взять скажем 88 или сто). И делаем это на L3-L4, на уровень приложения не выходя (это уже для других систем).
В норме современные сетевухи нам с помощью RSS раскидывают трафик сразу по целевым ядрам, беря хэш от тупла src address:port - dst addr:port, но 1) полагаться на этот несовершенный механизм в 100% случаев нельзя, 2) в случае в ядре, шедулер таки может нас между ядрами CPU мигрировать, и 3) есть shared state и между соединениями.
Вот о последнем и уточнения. Одной из базовых крутилок для админа такой системы является rate limiting - например, разрешить с IP 1.2.3.4 не более 300 килобит в секунду и не более 100 пакетов в секунду. Для чего надо держать стейт для алгоритма, например, token bucket/leaky bucket. Список таких IP может формироваться для правила и динамически, а правил может быть много.
Другим возможным действием для правила может быть не только проверка, в каком стейте сейчас tcp конкретного соединения (это самоочевидно), но и, например, разные действия в зависимости от того, счетчик пакетов и/или байт на соединении меньше/больше заданного числа (а то, может, злодей соединился на L3 реально без синфлуда, но сидит и молчит в час по байту, занимая ресурсы сервера) - если бы счетчики были только для статистики, они бы кидались в per-CPU array, а юзерлендный сборщик суммировал бы их по ядрам когда админ сказал, но нет, всё не так просто.
Ну и разные таймауты для протухания записей в таблице в зависимости опять же от стейта конкретной записи (чтобы не порубить редко общающийся ssh кастомера на его сервер более активными и мелкими http-запросами).
Пусть расчетный размер таблицы одновременно отслеживаемых соединений будет 100M (сто миллионов записей).

Ну, как тут акторной моделью в близком железу Си себе жизнь облегчить?.. :)

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

Сколько я ни думал, ничего путного придумать не смог (кроме буферизации записи, как в атомиках).

Чтобы акторная модель здесь помогала, нужен актор на каждое соединение. 100М — не потянет на одной машине даже эрланг, предел примерно в 50÷100 раз ниже.

Из хороших новостей: буферизовать запись (если 300Кб/сек — число все-таки более-менее условное, а не 301 — и датацентр взрывается) гораздо проще в условиях ограниченного, заранее известного, количества «сущностей» (тредов) — 16, 64, не суть. В такой ситуации, без потери общности, можно считать, что тред один.

Поэтому можно (вроде бы) выделить для треда write-only размеченную область сырой памяти и увеличивать счетчик для каждого соединения одной ассемблерной инструкцией. Параллельно можно запустить WatchDog, который будет только читать оттуда, и предпринимать соответствующие действия. Надеюсь, мне удалось передать хотя бы смысл идеи.

Что здесь имеется в виду под буферизацией записи, я не понял. 300Кб/сек - это не просто условное число, а вообще произвольное, задаваемое админом, и подобных правил может быть больше одного - на IP, на подсеть... Похоже, тут недопонимание по задаче остается.

Если на token bucket / leaky bucket смотреть абстрактно математически, то для 300 Кб/сек таймер раз в секунду наливает 300 тысяч токенов в ведро, а каждый пакет декрементит число токенов по своему размеру, если осталось больше нуля - пакет пропускается (к следующему правилу), если нет - дропается. Ибо по большому счету, наш главный бесконечный цикл принимая каждый пакет в теле, может его в конечном счете пропустить либо запретить.
В реальности, конечно, там никаких сотен тысяч таймеров нет, ибо дорого, в объекте ведра хранится последний таймштамп, кроме счетчика, и "доливаемое" значение рассчитывается из текущего таймаштампа.

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

TL;DR: https://www.erlang.org/doc/apps/erts/counters.htmlatomics тоже)

Имплементация довольно забавная: каждое значение — не просто value, а кортеж {exclusive_flag, value}, а дальше бесконечный цикл, который захватывает этот самый флаг, причем, насколько я понял, ради быстрой записи он вполне себе reentrant.

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

А вот с counters пока что-то не соображу, как применить их к описываемой задаче. Там еще к тому же массив счетчиков, а обычно для соединения или подобной сущности есть структура (инстанс объекта, если угодно), внутри которой помимо прочего имеются несколько счетчиков, а сами структуры лежат в дереве или обычно хэше, чтоб лочить только одну корзину (ключ по 5-tuple addrs/ports например).

примерно такой же, как доступен и на Си

Прям чудес я бы не ждал :) Это же достаточно низкий уровень, чтобы всё уже по сути упиралось в примитивы ОС.

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

Уже даже скорее не ОС, а процессора/компилятора - ОС их сама точно так же делает из этого. Сделанные вручную подобия мьютексов на compare-and-swap в цикле на, допустим, 5 попыток - я в случае eBPF видел (а потому что она позволяет держать не более одного мьютекса штатно, тут деваться некуда).

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

Зато понятный и простой в 90% задач. Бери больше, кидай дальше

Но сложные и быстрые системы на нём не построить

Но сложные и быстрые системы на нём не построить

Быстрые - сильно от имплементации зависит. Уж сколько было убийц C/C++

А сложные - так ООП как раз и является методом управления сложностью

Есть гипотеза, что этот метод управления сложностью немного того. С заводским дефектом.

Есть гипотеза

А есть практика, что по настоящему сложные системы написаны именно с применением ООП. LLVM например, GCC перешёл на C++, ядра виндов, макосика с применением C++, да и Linux хоть и на C, но с явным ООП

Самые крупные математические библиотеки написаны на фортране. Самые крупные банковские приложения написаны на коболе.

Самые крупные математические библиотеки написаны на фортране

Мы вроде про сложность говорили.. Хотя вот tensorflow на С++.

Самые крупные банковские приложения написаны на коболе.

Хорошая попытка, но нет. Из top 10 инвесткомпаний США минимум 2 (это те, про которые лично мне достоверно известно) применяют C#. Может где-то кобол ещё и остался, но скорее это уже почти байка.

Всего два года назад я патчил компилятор GNU COBOL для портирования кода биллинга одного крупного российского опсоса с Solaris/SPARC на Ubuntu/x86 (импортозамещение). Размер проекта примерно - миллион строк на Коболе, десять миллионов на Си. И это просто потому что он свежий, самое начало 90-х.

Да этот перец будто с другого этажа общается. Мы ему за баранов, он нам за Ивана.

«Жрать хотим, шашлык давай»….

Мы вроде про сложность говорили.. Хотя вот tensorflow на С++.

Нет, мы говорим вообще о другом:

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

Зато понятный и простой в 90% задач. Бери больше, кидай дальше

Но сложные и быстрые системы на нём не построить

Ну так перечитайте приведённую Вами же цитату.

Но сложные и быстрые системы на нём не построить

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

Сумбурно? Но так и рабоает эта капиталистическая рыночная можель в конктретной сфере здесь и сейчас.

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

PS: короче я все о глобальном пою))

Добра Вам.

PPS: и да я таки нашел решение — однажды вы все мои много уважаемые синьоры ( старшие програмисты, ведущие программисты проекта, техничесике директора в конце концов) выйдите на пенсию, — я надеюсь вы пойдеты в серьезные вузы, то есть в науку, в инстутуы стандартизации, и наконец наведете порядок в самой ее сйтие))

Я с вами еже ли шо, на подхате)

Все перечисленные примеры - очень сильно ограничены в выборе языков. Компилятор должен бутстрапиться сам собой, в ядрах ОС тоже нужен низкоуровневой язык (причем из набора многолетней давности, а с тех пор совместимость же). Кстати, слухи об ООП в ядрах сильно преувеличены - оно там не "есть", а бывает в форме не всякой, а нужной для конкретного места, в довольно ограниченном количестве мест.

Все перечисленные примеры - очень сильно ограничены в выборе языков.

Хотите другие - пожалуйста: V8, тот-же Chrome в качестве сложных подойдут?

Компилятор должен бутстрапиться сам собой, в ядрах ОС тоже нужен низкоуровневой язык (причем из набора многолетней давности, а с тех пор совместимость же). 

LLVM имеет фронты для большого количества языков. Может бутстрапиться с любого.

И о фронтах - имхо, фронт для компилятора писать без ООП - тоска смертная, я пытался.

 Кстати, слухи об ООП в ядрах сильно преувеличены - оно там не "есть", а бывает в форме не всякой, а нужной для конкретного места, в довольно ограниченном количестве мест.

Весь IOKit в макосике, WDK в виндах достаточно "бывает" ?
То, что плюсы не пролезли в Linux на замену эмуляции объектов на сях - личная придурь Великого Фина - его единственный тезис про exception в конструкторе вызывает некоторое недоумение - индустрия знает множество примеров применения подмножеств C++ для увеличения производительности (в том числе и отказ от exception и RTTI, как это сделано в LLVM и в том, что связано с ядрами ОС)

Хотите другие - пожалуйста: V8, тот-же Chrome в качестве сложных подойдут?

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

LLVM имеет фронты для большого количества языков. Может бутстрапиться с любого.

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

И о фронтах - имхо, фронт для компилятора писать без ООП - тоска смертная, я пытался.

На функциональном языке? Скриптовом?

Весь IOKit в макосике, WDK в виндах достаточно "бывает" ?

И какой у них размер относительно всего остального? Когда я слышу про ООП в ядре "на голом Си", я первым делом вспоминаю VFS, а не изолированные подсистемы.

То, что плюсы не пролезли в Linux на замену эмуляции объектов на сях - личная придурь Великого Фина - его единственный тезис про exception в конструкторе вызывает некоторое недоумение - индустрия знает множество примеров применения подмножеств C++ для увеличения производительности (в том числе и отказ от exception и RTTI, как это сделано в LLVM и в том, что связано с ядрами ОС)

У плюсов для системного программирования и особенно ядра множество других проблем - вот начиная прям с отсутствия единого стандарта на ABI (ага, мэнглинг). Rust, при всех его недостатках и проблемах в этой сфере, и то перспективнее выглядит, чем плюсы.

Вроде сложные, а как посмотришь на обилие и частоту дыр

Дело, конечно, в ООП )))

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

Исторически LLVM вообще непойми чем был )) и вроде с C++ и начинался.

На функциональном языке? Скриптовом?

На C. Скажу, что на плюсах дело пошло много веселее, именно из-за наследования и полиморфизма.

У плюсов для системного программирования и особенно ядра множество других проблем - вот начиная прям с отсутствия единого стандарта на ABI (ага, мэнглинг).

Проблемы менглинга в рамках одного компилятора нет от слова вообще. (На всякий, напомню, что Linux долгое время собирался исключительно GCC из-за его нестандартных расширений и конкретного поведения при некоторых UB), да и что GCC, что LLVM используют один и тот-же Itanium ABI, так что различия на сегодня если и есть, то только чисто косметические. (Фантазии MSVC рассматривать не стоит). Да и практика применения C++ в ядрах показывает несущественность этой проблемы.

Rust, при всех его недостатках и проблемах в этой сфере, и то перспективнее выглядит, чем плюсы.

Не думал, что Вы из этих

Дело, конечно, в ООП ))

Дело в описанном в посте - ООП не справляется с этой сложностью, вот что с ним не так в 2025. Поэтому смысл менять шило на мыло.

На C. Скажу, что на плюсах дело пошло много веселее, именно из-за наследования и полиморфизма.

Здесь так и напрашивается "надо было еще ассемблер взять, еще веселее бы пошло"!

Проблемы менглинга в рамках одного компилятора нет от слова вообще (На всякий, напомню, что Linux долгое время собирался исключительно GCC из-за его нестандартных расширений и конкретного поведения при некоторых UB)

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

что GCC, что LLVM используют один и тот-же Itanium ABI,

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

Не думал, что Вы из этих

Это ирония: настолько говно, что даже раст лучше.

Дело в описанном в посте - ООП не справляется с этой сложностью, вот что с ним не так в 2025. Поэтому смысл менять шило на мыло.

Ну так приведите примеры соизмеримых по сложности систем без ООП

Здесь так и напрашивается "надо было еще ассемблер взять, еще веселее бы пошло"!

И что же подходит для фронта компилятора по Вашему мнению?
Ксло, на C у меня тоже получилось, только несколько странно - пришлось реализовывать руками то, что и так есть в C++. Будучи переписано на плюсы стало логичнее и структурированее именно за счёт ООП.

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

Вы удивитесь, но совершенно спокойно линкуются объектники собранные clang и gcc (упреждая вопрос - да, без всяких extern c).

Пример прислать?

Это ирония: настолько говно, что даже раст лучше.

А что по вашему не "говно"?

да написаны на ооп, но дыряаые……………

, но дыряаые……………

Ну, думаю, началось ...

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

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

Я это выдумываю по вашему?

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

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

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

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

Про "что-то свое" - совсем не так. Излагающий "свое" не знает матчасть. А она хорошо изложена, начиная с Г.Буча. Он и его коллеги/соавторы прекрасно все разъяснили про это самое ООП.

Про "перпендикулярно" - лучше и не скажешь :) У автора "мнения" просто каша/винегрет в голове из компонентов ООП и потоков. Включаем в комплект к "откровениям" - "Коротко о потоках" :)...

Рискну дать новые определения

ООП - метод управления сущностями (данными, которые изменяются во времени, но сохраняют свою идентичность/клеточную мембрану и статичный инвариант/бизнес правила/ДНК, корректность изменений достигается ограничением изменений только через их методы). Эти простые правила ООП компилятор давно научился гарантировать. Плюс обязательно плюшки вроде позднего связывания, чтобы удобно было писать библиотеки. Библиотеки проверены и, значит, менять функционал нельзя, но можно расширять.

Акторная модель тот же ООП, но гарантирует сериализованность (нужную последовательность) изменений.

ФП вытесняет изменения наружу, в контекст и контекст управляет корректностью последовательности изменений.

Без гарантий сериализованности изменений очень сложно добиться многопоточности. И чем сложнее приложение, тем сложнее добиться.

Технически, акторная модель гарантирует последовательный, но неупорядоченный доступ. Это нельзя назвать сериализацией per se.

Второй вариант определения

ООП - метод управления сущностями (данными, которые изменяются во времени, но сохраняют свою идентичность/клеточную мембрану и статичный инвариант/бизнес правила/ДНК, корректность изменений достигается ограничением изменений только через их методы). Эти простые правила ООП компилятор давно научился гарантировать, но он не умеет гарантировать инвариант при одновременных изменениях. Можно и вручную, но в ООП с мьютексами все видят, что есть критическая секция - она защищена, но её существование очевидно

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

ФП вытесняет изменения наружу, в контекст и контекст управляет корректностью последовательности изменений.

Актор в дополнении к гарантиям ООП ещё гарантирует сокрытие критической секции, где инвариант нарушается:

  • Обработку сообщений без конкурентности (no concurrent access), хотя и частично одновременно*

  • Изоляцию состояния между обработками

  • Невозможность наблюдать промежуточные состояния извне

  • Асинхронность, гарантирующая отсутствие блокировок, которые помешали бы тотальности

    *актор гарантирует, что наблюдаемое поведение будет таким, как если бы сообщения обрабатывались последовательно. А как это достигается внутри - зависит от библиотеки. Хотя, в Erlang только последовательно.

💯

Что не так с ООП?

ООП не предоставляет композируемой модели для конкурентности из-за торчащей наружу критичной секции и из-за синхронности с риском потери тотальности.

А что это определение даст?

Вот ещё прикольный приём нашёл

Актор меняет состояние (короткая критическая секция) и отправляет вычисления в отдельную чистую функцию с гарантированным результатом, поэтому она уже не влияет на актор

Иначе, он совсем однопоточным становится.

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

Так если каждый актор - это green thread, а не настоящий, то кроме оптимизаций какая разница? Может и совсем однопоточным быть.

Защита от dead lock

Вынесу на верхний уровень из https://habr.com/ru/articles/920898/comments/#comment_28490530 а то Хабр деревья комментов плоскими делает.

В общем, имел я опыт с eBPF/XDP. Изначальный-то BPF, в BSD, на котором ныне tcpdump работает абсолютно везде - был очень хорош. Юзер дает выражение типа "host 1.2.3.4 and port 80", tcpdump его компилирует в ассемблер виртуального RISC-процессора (два регистра, 16 слов памяти, мануал по опкодам влезает буквально на 2-3 экрана), который загружается в ядро и после несложной валидации исполняется виртуальной машиной или (в наше время) JIT-компилится. Хотя этот код от юзера не доверенный, исполнять его безопасно - в нём нет джампов назад и т.д. ограничения, он выполнится за конечное время - не за-DoS-ит ядро, не получит доступа к лишнему в нём, и т.д

Когда в Линуксе сделали eBPF, предполагалось, что уж теперь-то заживём! Добавили 10 регистров, доступ к памяти, всё это проверяет верификатор на двадцать тыщ строк в ядре, добавим API для всяких полезностей, прежде всего так называемых BPF maps - типа лукап по ключу, произвольные структуры внутри... всё, юзер теперь может писать код, который точно не обрушит ядро! Безопасно! Стабильно! Не надо учиться, как писать ядерные модули! И еще и быстро работать будет!

На практике же (даже оставляя за скобками вопрос найденных дыр), язык к этом приделали только один - Си. Еще один тип "процессора" в выводе LLVM, типа. По факту получилось, что нельзя просто взять произвольного Си-программиста, и он сможет написать eBPF-программу - с тамошними ограничениями, требования к квалификации наоборот повысились, уметь работать не только с ограничениями API, но и воевать с верификатором. Например, я заводил багу в clang https://github.com/iovisor/bcc/issues/5062 - и ведь каждый в своем праве, clang оптимизирует, а верификатор не может убедиться, что данные в этом регистре указывают на валидную память пакета...

И это только начало - дальше ограничения API/ABI, мапы могут не всё, мьютекс можно держать только один, из структур данных недоступны даже связные списки... В результате для построения той самой таблицы соединений (conntrack) в виде каком НАМ надо, не LRU expire (такая мапа есть), а самому контролировать время удаления записи - хрен там! То есть приём с массивов голов связных списков по каждой секунде и просто перелинковкой её из одного списка в другой по решению над пакетов, а ходил бы по всему этому один-единственный таймер - сделать нельзя! Хотя это можно было бы сделать на чистом ядерном Си, без EBPF/XDP (see e.g. https://lore.kernel.org/bpf/20241010224708.67f18726@nuclight.lan/T/ and https://lore.kernel.org/xdp-newbies/871q07ggv0.fsf@toke.dk/T/#e98aa7356d5e94ce91a4cb4454fc4710163e1c50a mail threads).

И главное, реально чтоб на eBPF что-то достаточно большое и полезное сделать - приходится делать генерацию кода. Да, вот у нас от админа приходит JSON из веб-морды или парсинг несложного языка правил типа iptables, и мы из этого генерируем Сишный код, пропускаем через clang и грузим объектник в ядро - если данную задачу вообще получилось решить на eBPF, а для остального приходится костылить отправку части в юзерленд (AF_XDP)...

В общем, я даже пытался спроектировать свой BPF64, обратно совместимый (в отличие от eBPF) и не требующий сложной верификации, но сдулся где-то как раз на сопряжении BPF-программы с остальными частями системы - shared-сегменты памяти, атомики те самые, может и мьютексы... И теперь думаю, что байткоды вообще - тупиковый подход, надо конструкции уровнем повыше... вот если AST лисп-подобно, и верифицировать проще будет, и генерировать сишный код из AST опять же проще...

Поэтому, пусть даже это будет только кривая половина акторной модели - если пользователь сможет писать код на чем-то более высокоуровневом, чем Си, для юзера не будет требоваться знание мьютексов и т.п. (а в eBPF для реальных задач приходится костылять свои "мьютексы" по типу ручного CAS в цикле на 5 попыток) и при этом можно будет делать что-то реально практически полезное - это будет УЖЕ огромный шаг вперёд. Ну да, Erlang в ядро не затащить, каждое соединение актором не сделать - пусть их только 64 треда, допустим - но если сделать нечто, позволяющее обходиться без явной синхронизации и дедлоков, а небольшой локальный кэш каждого CPU core вполне себе как локальные данные актора - нам на практике хоть станет возможно хотя бы из бронзового века в железный перейти. То есть я на примере данной конкретной задачи хотел бы обобщать в притаскивание полезных свойств акторов на Си в ядро.

Критика абсолютно обоснована для традиционного подхода. Однако утверждать, что вся объектная модель обречена в многопоточном мире, неверно. Проблема в модели разделяемого изменяемого состояния, а не в объектах самих по себе. Альтернативы, вдохновленные как изначальными идеями Кея, так и функциональным программированием, показывают путь вперед. Среда, построенная только на классических мьютексах, действительно имеет фундаментальные недостатки, и индустрия активно ищет и внедряет более безопасные модели параллелизма.

Чего там искать-то? Акторной модели больше 40 лет, эрланг её реализовал на близком к идеальному уровне.

Однако утверждать, что вся объектная модель обречена в многопоточном мире, неверно. 

Реальный параллелизм всегда сложен и, по своей природе, потенциальные дедлоки ему всегда сопутствуют при появлении взаимного влияния частей системы. Даже в пропагандируемой автором акторной модели дедлоки возможны: A ждёт ответа от B а B - от A.

Подход с иммутабельными объектами безусловно решает ряд проблем, таких как внутренняя консистентность объекта, но тянет за собой местами излишние копирование на каждый чих и при этом не является единственно возможным подходом. Это спор скорее религиозный. (Во многих реальных случаях изменение объекта на spinlock для сохранения консистентности не оставит конкурентам ни малейшего шанса в вопросах производительности)

Серебряной пули нет (с)

Автор не понимает, что любая абстракция (ООП в том числе), так или иначе всегда будет приземляться на реальное физическое железо у которого присутствует необходимость в синхронизации некоторых действий.
Таже самая очередь сообщений хоть выглядит идеальной иммутабельной абстракцией, но на самом деле объекты её синхронизации относятся "к внешней", по отношению к акторной модели, среде выполнения, но физически никуда не деваются.

«Автор не понимает» — это прям по Крылову, хотя я не очень-то претендую на титул слона.

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

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

Так можно и про мьютексы сказать - "словить дедлок можно только ожидая уже захваченный мьютекс". Однако такое определение счастья не приносит.

что банально отлавливается простейшим тулингом.

Подождите, мы вроде про парадигмы говорим, а не про некоторый тулинг, который чёто там может отловить (или не отловить).

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

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

Кроме того, это жесткий 100% дедлок, который будет замечен первым же тестом.

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

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

Связи могут быть длинные A ждёт B, B - C, C - ...., ... - Z, Z-A. И усё.

Кроме того, это настолько антипаттерн,

Понятно. Парадигма не обеспечивает. Ужос-Ужос!

Связи могут быть длинные […]

Какое именно слово вам непонятно во фразе «все сообщения асинхронны, поэтому создать дедлок можно только дожидаясь сообщения от самого себя изнутри обработчика предыдущего сообщения»?

Парадигма не обеспечивает. 

Ну, когда люди начинают в дискуссии юродствовать, всем всё становится понятно.

Какое именно слово вам непонятно во фразе

Понятно, что по существу у Вас аргументов не имеется. (((

Ну, когда люди начинают в дискуссии юродствовать, всем всё становится понятно.

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

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

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

По существу я сказал, что в длинных связях дедлоков не бывает,

Уже показывал дедлок в длинной связи:

Связи могут быть длинные A ждёт B, B - C, C - ...., ... - Z, Z-A. И усё.

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

В акторной модели все сообщения асинхронные. А не может ждать B по определению. Отправил и забыл.

Послушайте, ну перестаньте на секундочку держать собеседника за идиота,

Вас? Да упаси хосподи! Уж точно кем- кем, а идиотом Вас считать ну совсем нет никаких оснований! Манипулятором - возможно, но не идиотом.

Отправил и забыл.

Запросил значение и результат не интересен? А тут вдруг тот у кого запросили значение пришёл обратно (через длинную цепочку, с условиями, но так сложились звёзды...), и для ответа требуется запрошенное значение... Грусть-тоска.
Так, конечно, в мире где каждая транзакция не менее 50М (Зибвамбвийских?) не бывает, но жизнь немного сложнее hello world, и по этому ответ на вопрос "обеспечивает ли акторная модель отсутствие дедлоков" однозначен - нет.
Значит ли, что акторная модель не применима? Конечно не значит.
Более того, она является пожалуй наилучшей (из универсальных) при реализации взаимодействия между процессами, идущими на разных ядрах, но как указывал уважаемый @rsashka, замечание которого про то, что сколько гринтредов ни крутятся в рамках одного треда ОС - подход к примитивам взаимной синхронизации совершенно иной, при этом в большинстве случаев примитивы синхронизации вообще не требуются, к сожалению, остался без должного внимания, хотя это лучший вариант с точки зрения обеспечения производительности.

Запросил значение и результат не интересен? А тут вдруг тот у кого запросили значение пришёл обратно (через длинную цепочку, с условиями, но так сложились звёзды...), и для ответа требуется запрошенное значение... Грусть-тоска.

Интересен. Асинхронно. Работать, как было задумано, этот барон Мюнхгаузен не будет, но и дедлока не будет тоже. Просто актор-уроборос, который должен что-то сделать в ответ на результат, никогда этого не сделает (по очевидным причинам). Но всё остальное продолжит делать как раньше. Тривиальная аналогия: пейджер. Никаких гарантий, что вам перезвонят, — нет, но ситуации «я набираю ваш номер, вы — мой, и мы оба слышим короткие гудки» — не случится.

в мире где каждая транзакция не менее 50М

Ну не надо, право слово, передёргивать-то. Квантор всеобщности я в эту сентенцию не приносил.

[…] наилучшей (из универсальных) при реализации взаимодействия между процессами, идущими на разных ядрах […]

В виртуальной машине еще.

Осторожно, не попадитесь на его очередные вольные интерпретации.

В акторной модели все сообщения асинхронные. А не может ждать B по определению. Отправил и забыл.

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

это все равно будет взаимной блокировкой

Это с каких херов это вдруг будет блокировкой-то? Актор (оба актора в данном случае) продолжат исправно выполнять всю свою остальную работу. Да, если не нажать на газ — трактор не поедет, но «взаимная блокировка» тут-то каким боком?

Вы уже совсем чушь стали нести, в праведном желании доказать правоту.

И памятка на будущее, вдруг когда-нибудь окажетесь в приличном обществе: говорить в третьем лице о «присутствующих» — жлобство и признак убогого воспитания.

жлобство и признак убогого воспитания

Оффтоп: ну уж Вам лучше этой темы не касаться ))

Мне-то? Это почему еще? Хулиган и грубиян — это вообще не то же самое, что невоспитанный жлоб.

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

Я вообще в шоке от некоторых его перлов. Вроде бы взрослый человек, но самомнение и тупость из некоторых комментариев просто фонтанирует.

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

P.S.
Насчет херов блокировок, гуглите "Динамическая взаимоблокировка" (livelock).

Livelock невозможен в отсутствие shared resource, которых, увы и ах, нет в акторной модели.

Опять мимо. Рекомендую оперировать терминами, про которые вы знаете не понаслышке, если таковые существуют, конечно.

Articles