Pull to refresh
21
46
Alexander Kornilov @akornilov

Пользователь

Send message

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

Ценю ваш сарказм :) ответил выше.

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

Предлагаю Оккама оставить в покое и посчитать на пальцах сущности до и после: были mutex, lock guard и condition variable + защищаемые данные, стало SharedState + защищаемые данные, которые могут быть отдельной структурой/классом или просто строкой, например. Т.е. мы уменьшили общее количество сущностей, которыми вынуждены были манипулировать и получили одну новую абстракцию, которая объединяет общие данные со средствами их защиты. А правила всегда будут чем бы вы не пользовались :)

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

Любой класс с двумя шаблонами N и K может сгенерировать N * K вариантов, если видите в этом проблему, тогда конечно, видимо, лучше не пользоваться шаблонными классами :)

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

Писать код "не думая" не думал даже рекомендовать - вы, видимо, как-то превратно трактуете прочитанное :)

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

Насчет "чертовщины", пожалуйста, в церковь :)

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

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

Ну а почему, собственно, вопрос дурацкий? Ошибки в многопоточном коде допускают очень многие люди. Если вы давно уже нет, поздравляю от всей души, но позволю усомниться такому самоуверенному заявлению :)

P.S. Раз статья вам не пригодилась вежливость требует возвратить вам лучи известной субстанции от благодарного автора :)

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

Ахринеть, дайте два.

Увольте пожалуйста от такой "камасутры" :)

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

Именно это и должен понимать потенциальный пользователь выбирая подобный подход.

Совершенно верно.

Если пользователь хочет лочить вручную то просто использует мьютексы напрямую.

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

Выбор за разработчиком.

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

Во-первых, нас никто не вынуждает.

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

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

Так не успел я :) только идею озвучил, а вы уже код сразу выкатили в следующем посте :)

Это не решение. И мне кажется, что от слова совсем.

А... ну раз кажется тогда, конечно :)

То, что у вас всегда внутри SharedState живет condition_variable, -- это
еще один недостаток вашего подхода. Т.к. нарушается принцип не платить
за то, что не используется.

А почему вы заранее решили что это не будет использоваться? Да еще сразу же в недостатки записали :)

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

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

А в каком контексте там на этот документ ссылаются? Может это вообще не имеет никакого отношения к обсуждаемому здесь вопросу?

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

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

Странно было бы, если бы вы думали иначе.

Я просто привел аргументы в пользу своей точки зрения.

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

Вы видите потому что я вам сказал что они лочатся :)

Я то как раз предлагал перестать мыслить в рамках лочить-разлочить.

Но, конечно, ни в коем случае никому не навязываю свое видение :)

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

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

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

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

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

Ну не нужно горячиться :) Какие личные проблемы? Вы о чем вообще? Вы что психоаналитик? :)

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

Ну раз непонятно, значит ваш SharedState для таких сценариев не подойдет.

Зачем же так категорично? :) Может и подойдет.

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

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

У меня есть устойчивое подозрение, что вы просто не осознаете, зачем нужен std::lock и почему он был добавлен.

Напрасно подозреваете, там все очевидно :)

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

Ну вы же привели кусок кода? Так вот вы все правильно поняли это и есть одно из решений.

Так что это не столько достоинство вашего подхода, сколько последствия его особенностей.

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

Вон в Java прямо в базовый Object добавили wait/notify/notifyAll :)

Кстати говоря, ваш SharedState всегда сопровождается condition_variable (event)?

Да.

Ну да, ну да. Это не лок. Выглядит как лок, работает как лок, но не лок.

Хорошо давайте посмотрим на это так - вы смотрите код и видите "залочить мютекс". Какие данные он защищает? Когда его можно разлочить?

Или вы видите в коде "читаю данные". И смотрите на небольшой блок кода в котором они читаются.

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

Одной синхронной операцией модифицировать сразу несколько независимых друг от друга SharedState.

Это я понял, мне непонятно зачем может понадобиться одновременно лочить независимые SharedState? Цель то какая? Выглядит как какой-то костыль при кривом дизайне системы.

Может нужен просто еще один SharedState в котором будут собираться данные из остальных?

Либо вариант, который вы уже предложили.

Либо внутри блока доступа к одному SharedState открыть остальные на чтение или запись.

Как я понимаю, ваш SharedState дает пользователю две полезные вещи

Еще можно дожидаться определенного состояния данных и нотифицировать об этом.

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

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

Это скорее концептуальные преимущества, а не технические.

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

Не понятно. Вы что-то такое подразумеваете

Да, только внутри DataB нужно, наверное, поместить хотя бы еще один SharedState иначе непонятно зачем такой огород городить :)
Может вы объясните какой юзкейз мы пытаемся покрыть в данном случае? Тогда что-нибудь поизящнее попробуем выдумать :)

Я бы не стал утверждать что это "обычно", это разве что в Windows так :)

Но посмотрите, например, на Mutex из Rust-а: там объединение mutex-а и защищаемого им объекта T происходит на уровне типа Mutex.

Спасибо, хороший пример. Действительно здравая идея объединить mutex и защищаемые данные. Только еще condition variable не прикрутили :)

У тупых mutex-ов, при всех их недостатков, такой проблемы нет. Если вы
mutex захватили, то результаты ваших вычислений устареть не могут, т.к.
исходные данные никто не может модифицировать.

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

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

Вложенные SharedState.

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

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

Кто это сказал?

Я, но это была шутка :) ваша мысль мне понятна.

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

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

Походу "не завезли" :) но думаю макросом можно решить вопрос :)

1
23 ...

Information

Rating
109-th
Location
Нижний Новгород, Нижегородская обл., Россия
Works in
Date of birth
Registered
Activity