Как стать автором
Обновить
0
2.7
Виктор Поморцев @SpiderEkb

Консультант направления по разработке

Отправить сообщение

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

Т.е. прикладник всегда должен видеть узкие места - где минимальными усилиями получить максимальный выигрыш в производительности. По всей задаче в целом.

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

Но тут речь идет о работе с памятью. Или вы про in-memory DB? Так есть альтернативы в виде lock-free алгоритмов. Те же конкурентные очереди, деревья, списки...

Как пример

Вопрос всегда в том - для вашей конкретной задачи это действительно нужно? Скорость доступа к данным для вас действительно является узким местом?

В моей практике таких задач было по пальцам пересчитать (за 30+ лет). В подавляющем большинстве случаев необходимость многопоточки была связана с большими затратам времени на обработку данных. Т.е. ускорив доступ к шаренным данным в 2-3 раза, я получу суммарный выигрыш в производительности всей системы в доли процента. Тут просто овчинка выделки не стоит - берем любой системный канал связи (пайпы, сокеты и т.п.) и легко и быстро реализуем на нем обмен данными между потоками/процессами. А конкурентность доступа уже обеспечивается системой.

Попытки рассуждать вслух вокруг да около.

А вы все задачи решаете однотипно? Без учета конкретики и граничных условий? Все копипастой старых решений? И никогда "на берегу" не задумываетесь "а что будет если..."?

Да куда уж мне, я и программировать-то не умею.

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

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

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

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

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

Что именно вам кажется ерундой? Контроль над данными с которыми осуществляется совместная работа несколькими потоками? Идея изоляции шареных данных?

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

Я уже писал тут, что в зависимости от задачи можно выбирать иные подходы. И многопоточная обработка не всегда требует работы с одним массивом шареных данных. А если и требует - см. любую операционную систему где 10 программ могут работать с одним файлом, но все делают это через системное API (все ваши read/writeв конечном итоге приходят в одну точку в ядре системы). И все проблемы конкурентного доступа и блокировок решаются в одном месте - на уровне ОС. А системное API отдает в программы уже безопасную копию данных в их текущем состоянии.

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

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

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

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

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

И все равно, когда потоков становится не 2, а 5, а мьютексов не 3, а 10, вы все равно рискуете за всем не уследить, а дальше чем выше плотность обращений к данным, тем выше вероятность попадания в дедлок. И тут возникает необходимость реализации механизма таймаутов доступа и разрешения коллизий. И вероятность провалов производительности на ровном месте. Что не всегда допустимо по условиям задачи (там может быть условие не столько быстродействия, сколько гарантированного времени реакции и отсутствие фризов).

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

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

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

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

Т.е. можно будет относительно безболезненно отстреливать приболевшие части сервиса в виде его отдельных процессов, не пытаясь починить их погулявше поврежденные heap/stack и прочие local state.

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

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

Если в рамках нескольких процессов и расшареной памяти - то да, только системные объекты синхронизации (мьютексы, может быть где-то как-то семафоры)

Если все(!) потоки обращаются к данным только на чтение

Не совсем понятно тогда как это работает? Есть все только читают, то вообще никаких проблем нет.

А если чтение-запись, то, мне казалось, это решается через critical sections скорее, нежели через мьютексы...

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

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

Да, верное решение. Геттер/сеттер которые работают с данными, защищенными мьютексом.
Как это будет реализовано уже не суть важно - класс, лямбда... Важно что вы не обращаетесь к данным напрямую, только через get/set, а те уже внутри используют мьютекс.

Ну тут что есть под рукой :-) Пайпы в принципе очень просты в работе и не грузят систему.

Но у нас на платформе есть User Queue - системный объект (т.е. никаких библиотек - поддерживается системными средствами). Преимущество в том, что его не надо каждый раз создавать-удалять. Один раз при развертывании поставки создал (с нужным именем) и оно есть. Только подключайся. Оно может быть FIFO, LIFO или KEYED - когда каждый пакет еще дополнительно снабжается "ключом" и можно этот ключ использовать в качестве условия для извлечения сообщения (равно, не равно, больше, меньше, больше или равно, меньше или равно) - извлекается первое сообщение, подходящее под условие. Основное преимущество перед пайпом - есть возможность "материализации" - получения состояния очереди (в т.ч. максимально возможное количество сообщений и текущее количество сообщений) что позволяет контролировать скорость раздачи и разбора и динамически балансировать систему (если очередь растет - добавить обработчик, если уменьшается - остановить какой-то из обработчиков). А поскольку это системный объект, то даже в случае падения задания (головного, обработчиков) содержимое очереди сохраняется в памяти системы.

Есть еще Data Queue - примерно тоже самое, но более тяжелая за счет того, что хранит все содержимое свое на диске.

И та и другая очереди доступны как через API, так и через SQL

Хотим посмотреть информацию об очерелди

select *
  from table(USER_QUEUE_INFO('TSTQUE'));

Получаем

Хотим посомтреть содержимое (без удаления из очереди - "материализация сообщений", peek)

select *
  from table(USER_QUEUE_ENTRIES('TSTQUE'));

Получаем

Для сопровождения очень полезно

Это так. У меня жена профессиональный переводчик. И много времени жила "там" (США, Канада, Британия) по работе. И свободно говорит на том языке, на котором думает. И думает на том языке, в какой среде находится. До смешного - иногда сначала в голове формируется на английском (потому что именно эта мысль на английском точнее и лаконичнее), потом уже на русском ("я знаю как это по-английски сказать, но что-то сходу в голову не приходит как на русском адекватно сформулировать").

Язык - это не только слова, это образ мысли. Убогие мысли - убогий язык. Неумение четко сформулировать мысль выражается в "э-э-э..." в языке.

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

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

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

  • "конвейерная обработка потока данных"

  • "параллельная обработка большого количества независимых элементов".

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

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

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

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

В обоих случаях нет нужды связываться с разделяемой памятью и синхронизацией доступа к ней. Можно воспользоваться системными средствами (и за всю синхронизацию будет отвечать система). Для конвейерной обработки используется принцип "почтовых ящиков" - у каждого потока есть свой ящик (в Windows можно использовать mailslot, в иных системах - локальный именованный Unix socket) куда любой может писать блоки данных для этого потока.

При параллельной обработке можно использовать pipe в который головное задание пишет пакеты, а обработчики читают их оттуда. Ну или если истема поддерживатье что-то еще подходящее (сейчас вот с IBM i работаю - там есть очереди - data queue и user queue - ккк раз для такого удобно).

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

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

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

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

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

Да ну? А в заголовке вижу про "FAR - центр вселенной".

Любите понятия подменять? Придумать якобы аргумент оппонента и потом победоносно его опровергнуть? Ну тоже такой прием с дискуссиях есть, бывает...

И еще раз - почему именно FAR (а не DC, TC и т.п.) лучше чем cd+ls? Потому что вы другого не пробовали?

Т.е. разумных аргументов нет?

Еще раз. Почему именно FAR? Почему не любой другой двухпанельный ФМ? В чем преимущество именно FAR перед остальными?

Во всей ветке из без малого 500 камментов я вижу лишь два -

  • работа с консолью

  • (субъективно) больше нравится текстовый интерфейс

Все. Остальные упираются в "FAR лучше чем cd+ls" или "FAR лучше стандартного проводника".

Так кто тут тупит?

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

По мне так все равно неудобно. С папкой же не всю жизнь работаешь, а какое-то время. Вот сейчас в работе 5 одних папок, завтра 7 других... Мне в этом плане DC нравится - две панели, на каждой по несколько вкладок с текущими в работе папками. При выходе он помнит что было открыто.

Закончил работу с папкой - закрыл складку. Начал работу с новой - открыл ее в новой вкладке.

Теперь у него есть свой настроенный workflow, который от ОС не зависит.

Ровно до тех пор, пока все ОС, с которыми работает, построены на одних концепциях (папки-файлы).

Т.е. на смогу? Я этим регулярно пользуюсь там, где это работает.

Копирование - или набирать вот такое

CPYF FROMFILE(ASRCPGM/CPOSRC100) TOFILE(ASRCD09/CPOSRC100) FROMMBR(*ALL) TOMBR(*FROMMBR) MBROPT(*REPLACE) CRTFILE(*YES)

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

Я в трех системах работаю - Win, Linux и IBM i (AS/400). И везде свои инструменты. Без зацикливания на чем-то одном. Так что некоторый кругозор имеем-с...

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

Это скорее дань привычки. В DOS, до появления NC, для копирования нужно было набивать команды на клавиатуре (равно как в линукс в терминале). Потом - куда на одной панели, откуда на другой и одна кнопка. Так и привыкли. Одной кнопкой копировать, одной кнопкой папку создать и т.п. Поиск, опять же...

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

Ну как-то так. Кому как удобнее, кто как привык. Остальное по большому счету - просто потрындеть.

1
23 ...

Информация

В рейтинге
1 034-й
Откуда
Екатеринбург, Свердловская обл., Россия
Работает в
Дата рождения
Зарегистрирован
Активность