Здравствуйте, WolfHound, Вы писали:
_>>Вообще то как раз показал, в одном из начальных сообщений. Причём что самое интересное (особенность именно Rust, т.к. в нём взятие даже небезопасной ссылки не требует unsafe): unsafe блок там может располагаться вообще в другом модуле кода, а в коде самого потока никакого unsafe нет. WH>То есть без unsafe не можешь.
Ох, вот всё тебе надо разжёвывать. Показываю на пальцах: имеешь в дальнем углу какой-то из используемых тобой библиотек например такую функцию:
fn hack_rust<T>(p: *const T)->&'static T
{
return unsafe {&*p};
}
И потом спокойно получаешь доступ к разделяемым данным мимо мьютекса в своём многопоточном коде:
fn main() {
let stolen: *const usize;
let protected = std::sync::Mutex::new(123usize);
// Scope of the mutex guard
{
let guard = protected.lock().unwrap();
let guarded: &usize = &guard;
stolen = guarded;
}
println!("Can read unprotected data: {}", hack_rust(stolen));
}
Как видишь, в коде твоего проекта не будет ни одного явного использования блока unsafe. И при этом никаких гарантий даже такой простейшей вещи, как доступ по мьютексу (хотя это само по себе не даёт никаких гарантий корректной многопоточности).
WH>>>2)Устроить дедлок можно почти на чём угодно. Например, если актор может синхронно обратиться к другому актору, жди дедлоков. А если ты реализовал акров на пуле потоков, то дедлоки тебя найдут ещё быстрее. _>>Синхронно обратиться — это как? Случаем не через тот же самый мьютекс? WH>Послать сообщение и подождать ответ.
В модели акторов сообщения посылаются асинхронно. )))
_>>Одинаково трудно отлаживается. Причём основная проблема в том, что очень трудно покрыть отладкой все возможные сценарии некорректного многопоточного кода. WH> Ты вообще хоть раз многопоточный код отлаживал? WH>Ловля гонок это исключительно внимательное чтение кода. WH>А если у тебя программа нарвалась на дедлок, то просто цепляешься отладчиком и дедлок как на ладони.
Это если ты можешь точно создать условия, при которых возникает дедлок. А он может возникать только в специфических условиях, когда допустим какой-то поток не успевает отработать или наоборот слишком быстро завершается.
_>>Так же как и в любом другом языке, за редкими исключениями (типа Erlang). И Rust к этим исключениям точно не относится — он не даёт никаких гарантий корректной многопоточности, как ты сам видишь по самому факту наличия этой ошибки. Понятно что её можно исправить (как и в любом другом языке), но если бы были гарантии корректности, то компилятор просто не позволил бы такой код. WH>Ещё раз. Данный код корректен. Нужно просто доработать библиотеку.
Угу, точно так же как и на C или Ассемблере. )))
_>>Хаха, вот явно ты вообще не в курсе этой области программирования. Как раз под мьютексами тяжёлое и делают. Потому как и сами мьютексы совсем не лёгкие (работают через ядро ОС). А если требуется именно быстрый лёгкий код, то применяют различные виды atomic'ов и т.п. CAS'ы. WH>Мьютексы они разные бывают. Вполне возможна реализация, работающая без обращения к ядру ОС. WH>В данном случае я про их реализацию вообще ничего не говорил. WH>Только про модель использования.
И про модель тоже не верно было. )))
Кстати, расскажи ка какие ещё реализации мьютексов по твоему применяются для синхронизации системных потоков. Ты же вроде предпочитал говорить исключительно о них. Или же мы всё же включим в обсуждение и всяческие реализации лёгких потоков? )))
_>>Ну давай, расскажи как например на том же Rust'е (в нём же есть метапрограммирование) изменить поведение конкретного экземляра данного типа, не модифицируя сам тип. WH>А зачем такое нужно?
Ну так ты же в предыдущем сообщение написал что: "1)Это можно делать на любых языках с метарограммированием. Динамическая типизация тут не нужна. Да и руками написать такое не сложно.". Вот меня и интересует как ты это сделаешь с помощью метапрограммирования на не динамическом языке, например на Rust'е.
WH>>>Мне не нужен кусок говнокода, который ничего не делает. WH>>>Мне нужна реальная задача, при решении которой такое нужно. _>>Понятно. Ожидаемый слив. ))) WH>С твоей стороны однозначный слив. WH>Придумать такой говнокод и я могу. WH>Вот чего я не могу, это придумать реальную задачу где такое нужно.
Ты серьёзно не можешь придумать задачу, в которой поток будет использовать целых две переменных, с разным временем жизни? Скорее на практике трудно встретить задачу, в которой не будет такой ситуации. Потому как в реальности данные обычно инициируются не в одном месте, а где-то по ходу исполнения приложения.
WH>>>Защитники динамической типизации тоже любят показывать несколько строк говнокода, который невозможно один в один повторить на большинстве статически типизированных языков. WH>>>Но когда у них спрашиваешь про задачу, которую они этим кодом собрались решать сливаются в 100%ах случаев. _>>Да, только я привёл пример не на динамическом языке, а на вполне себе статическом C++, который Rust в теории должен заменять. WH>Ты даже не понял, что я написал. WH>Перечитай раз 10. Может до тебя дойдет, что я привёл иллюстрацию твоего слива.
Пока что ты слился с реализацией простейшего кода в пару строчек на C++ (который Rust в теории должен заменять), причём в котором не было никаких низкоуровневых игр с указателями и т.п. опасных вещей. Однако пытаешься вилять, чтобы замять эту проблему.
_>>Реализация мьютекса в Rust (с контролем доступа к некому объекту) просто не нужная. WH>Ты не показал ни одного реального примера, где нужен другой сценарий.
Хы. Вообще то считай что весь мир целиком живёт по этому самому сценарию. Потому как во всех языках, библиотека, OS API и т.п. мьютексы работают именно на область кода, а не на область данных.
_>>Но если вдруг есть желание повторить её именно такой, то это легко делается на всех языках (при наличие доступа к классическому мьютексу). WH>Только гарантий времени компиляции ты не получишь.
Ну гарантии то и Rust не даёт. Но даже если бы и давал, то пользы от этого дела была бы весьма сомнительной.
Здравствуйте, alex_public, Вы писали:
_>Правда какое-то время назад он вроде забросил это дело и Влад2 теперь там наверное один пытается что-то вытянуть. А вот про то, что он после этого услышал про Rust и теперь неожиданно позиционирует себя как эксперта в нём — это действительно что-то новое. И забавное в том числе и потому, что когда мы тут на форуме 3-5 лет назад активно обсуждали Rust и D как раз в контексте потенциальной замены C++, от него не было ничего слышно (ну помимо того, что метапрограммирование в C++/D/Rust — это лишь жалкая поделка по сравнению с великими макросами Nemerle).
Я хоть и был со стороны противников Nemerle, вернее даже противников не самого немерле, а противников его сторонников, но согласен с выделенным
Скажем для rust даже сейчас с появлением процедурных макросов это все еще справедливо.
Вообще жаль что развитие Nemerle заглохло, если бы пошли по пути Kotlin (андроид и native) все могло бы быть и по другому.
Здравствуйте, alex_public, Вы писали:
_>Пока что ты слился с реализацией простейшего кода в пару строчек на C++ (который Rust в теории должен заменять), причём в котором не было никаких низкоуровневых игр с указателями и т.п. опасных вещей. Однако пытаешься вилять, чтобы замять эту проблему.
Одна опасная вещь там все-таки есть поток может пережить функцию main, и получим доступ к убитым локальным переменным. В Rust как раз поэтому и не соберется.
Здравствуйте, so5team, Вы писали:
S>Еще раз: речь не о блоках, от которых никуда не деться (ОС, компилятор, стандартная библиотека). Речь о том, что при написании прикладного кода никто не запрещает разработчику задействовать unsafe. Т.е. разработчики никуда не выходит из языка, не опускается в FFI, но "гарантии" идут в пешее эротическое.
И для системного или околосистемного языка это скорее плюс чем минус.
S>Так что если бы вы и WolfHound говорили о том, что Rust безопаснее и C++ и многих других языков, что Rust бьет по рукам разработчика при попытках использовать небезопасные конструкции, то предмета для спора не было бы. Но ведь вам важно накормить всей собственной верой в наличие именно гарантий.
Я с этим согласен, гарантий по большему счету нет.
Но я бы не недооценивал то, что вся грязь очень сильно изолируется в unsafe. Мне кажется что это вполне позволяет писать на rust с уровнем безопасности никак ни хуже чем у управляемых языков типа явы или шарпа.
Здравствуйте, FR, Вы писали:
vsb>> Код на Rust уж точно сложней писать, чем код на C++.
FR>Не сложнее, даже с учетом что новые стандарты С++ местами очень сильно упростили программирование.
Не верю. C++ это простой язык. Ты просто берёшь и пишешь что хочешь. Хочешь связанный список написать — просто берёшь и пишешь без всяких заморочек:
struct Node {
int data;
Node *next;
};
void insert_node(struct Node *node, int data);
...
В Rust ты кучу простейших структур данных просто не выразишь из-за всех этих владений. Как тут код писать? С языком будешь бороться, а не код писать.
vsb>Не верю. C++ это простой язык. Ты просто берёшь и пишешь что хочешь. Хочешь связанный список написать — просто берёшь и пишешь без всяких заморочек: vsb>
vsb>struct Node {
vsb> int data;
vsb> Node *next;
vsb>};
vsb>void insert_node(struct Node *node, int data);
vsb>...
vsb>
Что то С++ тут не пахнет, это практически чистый си.
vsb>В Rust ты кучу простейших структур данных просто не выразишь из-за всех этих владений. Как тут код писать? С языком будешь бороться, а не код писать.
В rust односвязный список ничем ни будет отличатся от такого же спска в C++ или Си.
Разве что страшным может показаться что-то вроде Option<Box<Node>> но в ассемблере это будет аналогично Node*.
Вот двусвязный список да это уже проблема написать без unsafe и аналогично C++. Но это одна из немногих структур
данных которые на rust выражаются сложнее чем на С++.
Здравствуйте, FR, Вы писали:
FR>В rust односвязный список ничем ни будет отличатся от такого же спска в C++ или Си. FR>Разве что страшным может показаться что-то вроде Option<Box<Node>> но в ассемблере это будет аналогично Node*.
Ну покажи пример такого односвязного списка. Зацикленного. Может я чего-то в корне не понимаю.
Здравствуйте, alex_public, Вы писали:
WH>>То есть без unsafe не можешь. _>Ох, вот всё тебе надо разжёвывать. Показываю на пальцах: имеешь в дальнем углу какой-то из используемых тобой библиотек например такую функцию:
То есть без unsafe не можешь.
WH>>Послать сообщение и подождать ответ. _>В модели акторов сообщения посылаются асинхронно. )))
Да это не важно. Если у тебя есть операция, которая ждёт конкретное сообщение, то дедлоки за тобой уже выехали.
_>Это если ты можешь точно создать условия, при которых возникает дедлок. А он может возникать только в специфических условиях, когда допустим какой-то поток не успевает отработать или наоборот слишком быстро завершается.
Делаем дамп зависшего приложения и смотрим.
Тоже мне проблема.
WH>>Ещё раз. Данный код корректен. Нужно просто доработать библиотеку. _>Угу, точно так же как и на C или Ассемблере. )))
Нет. На С у тебя программа расстреливает память и разлетается на куски.
А тут приложение зависает, не портя память.
Более того эту конкретную проблему можно починить сделав планировщик более умным.
WH>>Мьютексы они разные бывают. Вполне возможна реализация, работающая без обращения к ядру ОС. WH>>В данном случае я про их реализацию вообще ничего не говорил. WH>>Только про модель использования. _>И про модель тоже не верно было. )))
Что не верно? Под мьютексом у нас живёт разделяемый ресурс.
Если его захватывать надолго, то все потоки будут отвисать.
_>Кстати, расскажи ка какие ещё реализации мьютексов по твоему применяются для синхронизации системных потоков.
Спинлоки. Если ты захватываешь мьютекс на небольшое время, то они отлично работают.
_>Ты же вроде предпочитал говорить исключительно о них. Или же мы всё же включим в обсуждение и всяческие реализации лёгких потоков? )))
Откуда ты это взял?
_>Ну так ты же в предыдущем сообщение написал что: "1)Это можно делать на любых языках с метарограммированием. Динамическая типизация тут не нужна. Да и руками написать такое не сложно.". Вот меня и интересует как ты это сделаешь с помощью метапрограммирования на не динамическом языке, например на Rust'е.
Генерируем обёртку и работаем через неё. Можно даже сделать интерфейс, который делает объекты неразличимыми.
А теперь ты отвечай, зачем нужен такой идиотизм.
_>Ты серьёзно не можешь придумать задачу, в которой поток будет использовать целых две переменных, с разным временем жизни? Скорее на практике трудно встретить задачу, в которой не будет такой ситуации. Потому как в реальности данные обычно инициируются не в одном месте, а где-то по ходу исполнения приложения.
Серьёзно. Я такое ни разу не видел. Ни разу про такое не слышал. И не слышал ни одной жалобы на дизайн мьютексов в rust. Видимо ты один кто так мьютексы использует.
И раз у тебя такое происходит постоянно, то ты должен был легко показать реальный пример.
Но ты всегда уходишь от ответа на вопрос зачем. Ибо ты не знаешь. Просто придумываешь говнокод ради спора.
Или может, понимаешь, что нагородил говнокод, и коллеги засмеют?
_>Хы. Вообще то считай что весь мир целиком живёт по этому самому сценарию. Потому как во всех языках, библиотека, OS API и т.п. мьютексы работают именно на область кода, а не на область данных.
А по факту их всегда используют на область данных.
Исключений я не видел.
_>Ну гарантии то и Rust не даёт. Но даже если бы и давал, то пользы от этого дела была бы весьма сомнительной.
Без unsafe даёт.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, FR, Вы писали:
FR>Я с этим согласен, гарантий по большему счету нет.
Если как следует натянуть сову на глобус, то гарантий не может быть в принципе.
Ибо всегда может быть ошибка в компиляторе или стандартной библиотеке.
Например я видел как багланд С++ дебилдер два раза вызывает деструктор объекта. А в другом случае вообще не вызывал.
Последствия, думаю, тут все понимают.
FR>Но я бы не недооценивал то, что вся грязь очень сильно изолируется в unsafe. Мне кажется что это вполне позволяет писать на rust с уровнем безопасности никак ни хуже чем у управляемых языков типа явы или шарпа.
На самом деле уровень безопасности гораздо выше.
1)В шарпе тоже есть unsafe.
2)В шарпе нет защиты от доступа к разделяемым данным.
Собственно rust по тому и возник что ребята из мозила хотели писать очень параллельный код но на С++ не получалось. Программа разлеталась на куски. А на rust получилось.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, so5team, Вы писали:
S>Rust здесь лишний. В erlang-е вы в принципе не сможете передать ссылку на разделяемые мутабельные данные.
Ещё как можно. Это называется ETS: http://erlang.org/doc/man/ets.html
Здравствуйте, Cyberax, Вы писали:
S>>Rust здесь лишний. В erlang-е вы в принципе не сможете передать ссылку на разделяемые мутабельные данные. C>Ещё как можно. Это называется ETS: http://erlang.org/doc/man/ets.html
Спасибо. Значит и по отношению к Erlang-у термин "гарантии" нужно употреблять очень осторожно.
Здравствуйте, WolfHound, Вы писали:
_>>Конечно. Я нигде и не говорил, что Rust чем-то хуже других системных языков в области многопоточности. Я всего лишь опровергал известный миф о том, что он чем-то лучше. WH>Он намного лучше. В других языках весь код без исключения unsafe. А на rust весь код safe. WH>unsafe нужен очень редко для создания чего-то хитроумного. WH>Многие программы вообще можно написать, не используя unsafe.
Ты точно не заговорился и держишь в уме, что мы говорим не об управление памятью, а о многопоточности? Потому как в многопоточности никакого гарантированного safe в Rust нет. Причём даже в случае отказа от ключевого слова unsafe. )))
_>>Ну вот расскажи как получить взаимные блокировки например при полном следование модели акторов... WH>А моделей акторов в реальной жизни до чёртиков. WH>Сколько реализаций столько и моделей. WH>Если реализация позволяет актору ждать конкретное сообщение, то дедлоки за тобой уже выехали.
Ну так инструментом то всё же надо уметь пользоваться... Т.е. реализация многопоточности через акторы конечно же является мощными защитным инструментом, но не настолько, чтобы можно было посадить обезьяну и получать гарантированный код от её случайных нажатий. Хотя бы минимальное понимание как применять инструмент всё же требуется. )))
_>>Я думаю ты должен понимать, что такая ситуация с библиотеками является следствием не разного дизайна языков, а их разного исторического пути. WH>Тут именно разный дизайн языка. WH>В случае с rust легко делать safe библиотеки. Более того все ожидают что у библиотеки будет safe интерфейс. WH>А в случае с C/C++ всё держится на соглашениях.
В современном C++ тоже ожидается safe интерфейс. Проблема в том, что большинство популярных библиотек начало писаться задолго то принятия в стандарт языка этих safe инструментов. Не говоря уже о том, что в C++ очень часто используют напрямую C библиотеки, в которых естественно нет никаких намёков на это.
Грубо говоря, если бы стандарт C++14 был принят в 90-ые годы, то большинство C++ библиотек имело бы унифицированный безопасный интерфейс, а такие языки как D, Nim, Rust вообще не появились бы на свет за ненадобностью. Однако исторически сложилась другая картина и сейчас очень интересный момент для этих новых языков — они зародились для замены старого стандарта C++, но пока из разрабатывали появился новый стандарт C++, при котором они не особо то и нужны. Но какую-то небольшую аудиторию они занять смогли, да и ситуация с библиотеками C++ будет выправляться очень долго. Так что ситуация неоднозначная — посмотрим куда в итоге развернётся индустрия.
Лично под мой вкус подходят оба языка. Изначально я думал, что точно со временем надо будет переходить на Rust, только подождать пока там более менее устаканятся инструменты и библиотеки (с этим кстати до сих пор проблема). Ну а пока ждал, уже в современном C++ начали появляться новинки, обходящие Rust.
Но это так, небольшое отвлечение от темы. Мы тут обсуждаем Rust, а не C++ или чьи-то личные вкусы. )))
_>>Насчёт замены ты конечно прав. Только вот вместо попыток найти какие-то минусы у C++ (смешно на самом деле, т.к. их множество и при этом они давно всем известны), ты бы лучше продемонстрировал бы какие-то плюсы Rust. WH>Так я и демонстрирую. Просто ты заткнул уши и кричишь unsafe, unsafe, unsafe, unsafe,... WH>Причём совершенно не ясно, зачем ты это делаешь.
На самом деле я наверняка знаю про плюсы Rust'а побольше тебя. Там довольно много серьёзных вкусняшек, в том числе и в сравнение с C++. Нюанс в том, что ты в данной дискусии упираешь именно на ту область, в которой у Rust в реальности плюсов нет — в реализации многопоточного кода.
_>>Согласен. У Rust'а есть определённые преимущества. Правда за них приходится расплачиваться написанием некоторого лишнего синтаксического шума. WH>Ты его так ни разу и не продемонстрировал. А я тебя уже раз 10 просил. WH>Ну что слив засчитываем?
Вообще то уже был явно продемонстрирован один из примеров. Но он естественно не единственный. Кроме показанного Иваном можно вспомнить и про бесконечные unwrap. И ещё есть набор нюансов связанных не с реализацией логики, а исключительно с обслуживанием BC.
_>>И да, это относится только к управлению памятью. А например в той же многопоточности здоровенный талмуд будет и в C++ и в Rust... WH>В rust не будет.
Будет в любом языке. И толщина этого талмуда зависит исключительно от применяемой архитектуры многотпоточности, а не от выбранного языка. Скажем при использование модели акторов талмуд будет скорее всего самым тоненьким (в любых языка). При использование модели синхронизации разделяемых данных барьерами талмуд будет однозначно толще (в любых языках). А самый толстый будет наверное в случае применения раличных lock-free инструментов (опять же в любых языках).
WH>>>Ты уверен, что я? Если я правильно использую мьтексы то, какие могут возникнуть проблемы? Я же их использую правильно. _>>Конечно. И это полностью аналогично утверждению о том, что на голом C с его указателями можно написать быстродействующую и абсолютно корректную программу. Естественно при высоком уровне профессионализма и внимательном отношение к коду. Только вот народ что-то предпочитает переходить к автоматическому управлению типа C++/Rust... WH>Тебе осталось сделать ещё один шаг и понять, что к акторам это тоже относится.
Переход мьютексов к акторам — это приблизительно тоже самое, что переход с языка C на язык Java в области управления памятью. Как мы знаем, устроить проблемы с памятью при должном старании можно и в Java, но масштаб проблем совсем другой. Правда и цена за это платится существенная — потеря производительности и возможности реализации целого класса приложений. Как раз в точности как и у использования модели акторов.
_>>К сожалению в области многопоточности нет аналога автоматического управления памятью C++/Rust. Всё же немногие существующие автоматические решения в этой области (типа реализации модели акторов в Эрланге) позволяют это делать ценой потери производительности — т.е. они в каком-то смысле являются аналогами решений по памяти со сборщиками мусора. WH>Вот только в случае с ерлангом есть вполне известные проблемы. WH>Например, из-за наличия selective receive получение сообщения из очереди O(N). WH>И если вдруг в очереди окажется слишком много сообщений, то ерланг начинает тупить на разборе этой очереди. И отзывчивость системы сразу падает до нуля. WH>От дедлока мало чем отличается. WH>Всё что нужно для того чтобы это спровоцировать кратковременный всплеск нагрузки.
Ты весьма забавно пишешь эти "потрясающие откровения" про проблемы с производительностью у акторов, прямо в ответ на мою цитату с упоминанием проблем производительности. ))) И да, тормознутость и дедлок — это принципиально разные проблемы.
WH>>>Я начал говорить о том, что большинство языком эти свойства не гарантируют. _>>Свойства модели акторов гарантируются математикой, а не языками программирования. Ознакомься с теорией что ли... ))) WH>А реализуются они математикой или конкретным языком программирования?
Ещё раз повторюсь: корректная многопоточность является следствием именно данной архиктектуры, вне зависимости от языка (хоть ассемблер используй), при соблюдение построения этой архитектуры. Как ты будешь следить за тем, что правила архитектуры соблюдаются — это вообще другой вопрос (в большинстве языков это решается административно). А смысл применения этого подхода в том, что правила построения очень простые в сравнение с неформализуемым набором эмпирических правил построения корректной многопоточности произвольной архитектуры.
WH>>>В случае с rust имеем. Причем в случае с evmap гарантированно проблем не будет. _>>И опять же ты ничего не понимаешь в этой теме. Локфри контейнер — это всего лишь продвинутая разновидность atomic'а, которая не гарантирует корректную работу многопоточного кода. Единственное, что гарантируют такие контейнеры (как и любые atomic'и), это отсутствие наложения параллельных действий. Однако это не гарантирует корректности, потому как например тоже самое отсутствие наложения, может приводить к потерям части действий и т.п. WH>Ты даже не ознакомился с тем, что такое evmap, а мнение имеешь. WH>У evmap чтение транзакционное. WH>Все изменения живут внутри write handle, который существует в одном экземпляре. Изменения становятся доступны на чтение только после вызова refresh(). При этом потоки которые начали читать до refresh, будут продолжать читать старую версию данных. Потоки, которые начнут читать после refresh, увидят все изменения. WH>Короче уровень изоляции serializable без оговорок.
Ты тут кстати действительно оказался прав. Я не внимательно посмотрел в прошлый раз: увидев в начале описание утверждение о том что это lock-free структура (и поверив в это), сразу пошёл в конец смотреть графики и т.п. И пропустил важнейший момент в середине текста. Оказывается эти умники предлагают в случае нескольких писателей в эту, как бы "lock-free" сущность, использовать мьютекс! Такого я точно не ожидал... Такая детская хрень не то что любые гарантии акторов выкинет, оно ещё и производительность убьёт (ради которой и страдают с неудобными lock-free сущностями).
WH>>>Rust, pony, erlang,... _>>Про pony ничего не знаю. Erlang — это да, специфический нишевый язык. Rust — нет, не защищает. WH>Делаем на rust реализация акторов и вперёд с песней. Rust позволяет сделать safe интерфейс. Гарантии будут не хуже чем у ерланга.
Возможно. Надо смотреть конкретную реализацию. В любом случае целевые гарантии будут следствием применения данной модели, а не самого языка.
_>>таким образом, применяя модель акторов, мы получаем замену сложнейшей задачи на очень простую (которую элементарно можно отследить в коде приложения). WH>Только в твоих мечтах. WH>Особенно если на С++.
Да элементарно даже на C + WinAPI. )))
_>>Думаю там без проблем можно получить некорректное приложение. Оно не будет зависать, но зато будет иногда выводить некорректные данные. Потому как lock-free вполне позволяет такое при неаккуратном использование. WH>Код в студию. WH>Ничего у тебя с evmap не выйдет. WH>Единственная возможность сломать evmap, это сделать refresh внутри транзакции чтения. WH>Учитывая то, что у evmap только один write handle сделать это, мягко говоря, не просто.
С учётом того, что оказалось что evmap — это не lock-free, а требующая мьютекса хрень, думаю даже нет смысла обсуждать это. Т.е. да, некорректный код там не получишь — получишь тупо тормоза в лучше случае и дедлок в худшем.
_>>Аааа, вот ты про что. Я если честно даже и не читал какие конкретно контейнеры вписал там Иван. Я среагировал просто на тройную (это кстати для Rust'а ещё не сильно много) вложенность типов, а не на сами типы. ))) WH>Так в С++ будет то же самое если использовать умные указатели и опциональные типы. WH>Короче лицемерие на марше.
Только вот BC принуждает их использовать даже в тех случаях, когда они по факту не нужны. Т.е. в C++ в этом коде будет просто переменная, а в Rust'е придётся мутить обёртку ради удовлетворения BC. Ты же вот слился показать аналог простейшего C++ кода из нескольких строчек. Случаем не по той причине, что тебя BC послал с прямым аналогом? )))
_>>Гарантии отсутствия доступа к защищённому мьютексом объекту он действительно даёт (правда и то только при наличие административного запрета на использование unsafe по всему проекту). Только вот никакого отношения к гарантиям корректной многопоточности это не имеет. А только это и интересно на практике. WH>На практике дедлок очень простая и скучная проблема. Первый и последний дедлок у меня был около 20ти лет назад. Отладка заняла несколько минут. Просто посмотрел содержимое стеков потоков и всё стало ясно. Ещё минут 30 заняло исправление. WH>А вот доступ к памяти без синхронизации задача очень сложная и весёлая. Ибо все, что тебе известно это то, что продакшен разлетается на куски раз в месяц. В дампе полная каша, из которой ничего понять нельзя. Вот и думай, что это было. Гонки, выход за приделы массива, использование освобождённой памяти,...
Какой у тебя интересный опыт разработки многопоточных решений. Даже интересно было бы посмотреть на какие-то конкретные реализации... )))
Здравствуйте, ·, Вы писали:
_>>Я думаю ты должен понимать, что такая ситуация с библиотеками является следствием не разного дизайна языков, а их разного исторического пути. ·>Можешь показать хоть один более менее крупный проект C++ с безопасным кодом? Для rust такое есть. А пока получается как "на С++ можно писать безопасно, но мы не хотим", прям классика "я всегда могу бросить".
Я думаю что очень многие C++ проекты начатые после 2014 используют безопасные подходы. Т.е. технически это можно было делать и намного раньше, но это было не формализировано, не стандартизировано и т.п. А с приходом C++14 это уже просто норма языка, которую не соблюдают разве что следуя традициям проекта начатого давным давно. Насчёт крупности даже не знаю — мне как-то лень искать какие крупные C++ проекты появились в последнее время. В моих проектах как раз всё безопасно (ну не считая взаимодействия с C библиотеками, но это вынужденное зло), но ты их точно не найдёшь в открытом виде, так что вряд ли может служить аргументом.
_>>К сожалению в области многопоточности нет аналога автоматического управления памятью C++/Rust. Всё же немногие существующие автоматические решения в этой области (типа реализации модели акторов в Эрланге) позволяют это делать ценой потери производительности — т.е. они в каком-то смысле являются аналогами решений по памяти со сборщиками мусора. ·>Ты просто всё свалил в одну кучу. Вот и получилась чушь. Сборщики мусора не решают "[все] проблемы управления памятью", а вполне определённые, например, обращение к освобождённой или неинициализированной памяти, висящие объекты. Но не решают проблемы утечек памяти, например. Так и Rust не решает "[все] проблемы многопоточности", а позволяет защититься от вполне определённых проблем, например, data race. C++ не позволяет. Нужно ли это? Конечно, ибо data race довольно частая и труднообнаружимая проблема, и её решение значительно упрощает написание кода. Ровно так же и сборщики мусора — решают наиболее частую и вредную проблему.
Хы, ты совсем не понял аналогию. Смотри как выглядит в реальности. Можно чётко выделить 3 группы подходов в управление:
1. Полностью автоматическое и не требующее мозга для использование. Характеризуется пониженной производительностью и снижением области применения.
В управление памятью примером этого является сборщик мусора (например как в Java или ).
В управление многопоточностью примером этого является модель акторов (например как в Erlang).
2. Автоматическое управление (всё чётко проверяется компилятором), но требующее приложение мозга (программист должен указывать компилятору свойства объектов). Имеет нулевой оверхед и подходит для любой области.
В управление памятью примером этого являются "умные указатели" (как в Rust или C++).
В управление многопоточностью подобного решения пока к сожалению не существует!
3. Ручное управление — компилятор ничего не проверяет и вся корректность кода лежит целиком на внимательности программиста. Имеет нулевой оверхед и подходит для любой области.
В управление памятью примером этого являются "голые указатели" (как в C или вообще Ассемблере).
В управление многопоточностью примером этого является ручная расстановка барьеров по коду.
·>По поводу использования unsafe vs "правильный С++" — это опять словоблудие. Вот пришел тебе пулл реквест на review — в случае rust ты можешь сразу увидеть unsafe и насторожиться. Можно даже тулзы соответсвтвующим настроить, чтобы коммиты с unsafe реджектить или назначать более серьёзный review всей командой. В случае С++ любое изменение потенциально может поломать всё что угодно и нет никакого простого и однозначного способа отличить безопасные изменения от опасных.
Вот в том то и дело, что не сможешь увидеть. Потому что в Rust применение usafe блока не обязывает помечать функцию как unsafe. Так что по факту ты можешь просто вызвать в своём коде некую вполне себе прилично выгляющую safe функцию из какой-то посторонней библиотеки и тем самым неявно получить в коде своего приложения дикий unsafe код. Который на ревью никто не увидит.
Более того, в стандартной библиотеке языка мы можем увидеть такие милые функции как например std::mem:forget, которые позволяют натворить много чего весёлого. Да, и если что, forget тоже не помечена как unsafe функция...
Так что если реально хотим сидеть в безопасной зоне (хотя бы по памяти — про многопоточность даже заикаться не надо!), то на Rust тоже будет небольшой талмудик требований, а не одна строчка "без unsafe".
Здравствуйте, Иван Дубров, Вы писали:
_>>Непонятно только к чему ты спросил такую очевидную вещь. ИД>Я к тому что если ты не завершишь работу программы, у тебя потоки будут продолжать использовать local_shared.
1. Так конкретный приведённый C++ код корректный или нет? )))
2. Повторить на Rust можем или нет?
Здравствуйте, Иван Дубров, Вы писали:
_>>Ну во-первых, как видишь уже имеем проблему Rust'а, что он не даёт реализовать совершенно нормальный пример напрямую. ИД>Нет, не нормальный. В определенных ситуациях код будет продолжать использовать висящие ссылки (если потоки переживут вызов функции). Это как то, что я совсем не хочу сидеть и ревьювить в коде наших разработчиков. Потому что это весьма неочевидное поведение.
Что-то ты совсем заговорился. ))) Ты предложил рассмотреть в виде этой "особой ситуации" случай вылета приложения по исключению и при этом считаешь это неочевидным поведением, которое будет сложно отследить? )))
_>>А во-вторых, вообще то конкретно такая реализация не принципиальна — ну возьми вместо просто локальной переменной какой-нибудь Arc и т.п. И попробуй реализовать. Смысл моего примера был в одновременном использование двух переменных с разным временем жизни — абсолютно реальная ситуация, т.к. многие данные инициируются по ходу работы приложения. ИД>Эти примеры нельзя напрямую переносить в Rust, потому что в реальности будет какая-то другая задача. Если будет Arc, то данные, возможно, будут вообще неизменяемые и ничего синхронизовать не нужно. И так далее.
Эээ что? Откуда это ты изобрёл какую-то связь между Arc и иммутабельностью данных?
И так, у нас есть конкретный, работающий пример на C++. ОК, Rust не позволяет переписать его дословно, потому что ему не нравится использование локальной переменной в других потоках. Ну ОК, сделай её не локальной переменной а выделенной в куче (Arc) и всё — в чём проблема то? Вся эта борьба с BC вообще не имеет никакого отношения к тому нюансу, который я хотел продемонстрировать этим примером. Т.е. да, Rust должен был "слиться", но намного позже и совсем в другом смысле. Я не ожидал, что у всех участников дискуссии возникнет проблема даже с банальным повтором такого простейшего кода.
ИД>Да, есть определённые задачи, которые сложно решаются в Rust. Например, циклические структуры данных или так называемые self-referential структуры данных.
Угу, я помнится слышал разные анекдотичные истории про реализацию дерева на Rust. Но на самом деле с такими проблемами можно было бы жить, если бы это была цена за какие-то очень вкусные плюшки. Но пока вкусность кажется весьма преувеличенной... )
Здравствуйте, Иван Дубров, Вы писали:
_>>Я в курсе, что Rust позволяет отслеживать жизненный путь ссылки. Но гарантию корректности даже этого простейшего нюанса (который в реальности не гарантирует корректность многопоточности в целом) мы можем дать только в том случае, если административными методами (т.к. для данного действия не требуется unsafe блок) запретить операцию получения голого указателя из этой ссылки. Да, для использования этого указателя требуется unsafe код, но он может располагаться в совершенно другом модуле приложения и даже в какой-то библиотеке (например написанной на другом языке) — для передачи таких указателей опять же не требуется каких-то unsafe модификаторов на пути. Т.е. по факту мы можем написать некий многопоточный код, даже без всякого явного unsafe в коде и получить при этом доступ к объекту не из под мьютекса. Т.е. гарантий нет даже в такой мелочи, которая сама по себе даже при гарантированной реализации не даёт гарантированно корректной многопоточности. ИД>Не совсем. unsafe код пишется из того расчёта, что через safe интерфейс вокруг этого кода нельзя нарушить инварианты Rust. То есть, если ты пишешь код который в safe функции принимает левый указатель и внутри unsafe блока его читает -- то этот код некорректный.
Он конечно возможно и некорректный, но исключительно в административном смысле. Потому как компилятор переварит его без единого возражения. Более того, он может скомпоновать этот код в красивую библиотечку, которую будут распространять без исходников (хотя на самом деле сейчас большинство народа не смотрит исходники даже если они открыты). Понятна моя мысль? )
ИД>Поэтому, возвращаемся опять к исходной постановке: неважно, что там делает код во всей системе -- достаточно проанализировать код в окрестностях unsafe. "Административный метод" применяется на стороне unsafe кода, которого как я уже писал, у нас 5 штук на 200 тыс. строк кода.
А как ты поймёшь то, где у тебя unsafe код, а где нет? Вот я сделал вызов некой функции hack_rust (см. код в сообщениях выше) — это безопасно или нет? Как понять? А например использование std::mem::forget — это точно всегда безопасно? )))
Или по твоему если в коде твоего проекта не встречается ключевого слова unsafe, то это уже даёт тебе какие-то гарантии?
ИД>Конечно, наверняка есть ошибки и просчёты, например, в библиотеках, которые таки-позволяют сломать систему, если постараться. В конце-концов, ошибки в компиляторе и так далее. Но меня больше практическая сторона волнует: как большой группой писать код на языке без GC и не получать всякие разные интересные проблемы с памятью.
На самом деле для решения этой проблемы надо просто следовать определённым не сложным правилам. В любом языке.
Небольшой плюс Rust'а в том, что часть этих правил он контролирует на уровне компилятора, в то время как в других языках это надо делать отдельными статическими анализаторами или просто административно. Но не факт, что этот плюс настолько хорош, чтобы терпеть и побочные минусы.
Здравствуйте, alex_public, Вы писали:
_>Ты точно не заговорился и держишь в уме, что мы говорим не об управление памятью, а о многопоточности? Потому как в многопоточности никакого гарантированного safe в Rust нет. Причём даже в случае отказа от ключевого слова unsafe. )))
Точно. По крайней мере, ты так и не показал доступ к разделяемой памяти без синхронизации.
_>Ну так инструментом то всё же надо уметь пользоваться... Т.е. реализация многопоточности через акторы конечно же является мощными защитным инструментом, но не настолько, чтобы можно было посадить обезьяну и получать гарантированный код от её случайных нажатий. Хотя бы минимальное понимание как применять инструмент всё же требуется. )))
А как же гарантированное математикой отсутствие проблем?
_>В современном C++ тоже ожидается safe интерфейс. Проблема в том, что большинство популярных библиотек начало писаться задолго то принятия в стандарт языка этих safe инструментов. Не говоря уже о том, что в C++ очень часто используют напрямую C библиотеки, в которых естественно нет никаких намёков на это.
Даже "safe" интерфейс С++ держится на соглашениях.
_>Грубо говоря, если бы стандарт C++14 был принят в 90-ые годы, то большинство C++ библиотек имело бы унифицированный безопасный интерфейс, а такие языки как D, Nim, Rust вообще не появились бы на свет за ненадобностью. Однако исторически сложилась другая картина и сейчас очень интересный момент для этих новых языков — они зародились для замены старого стандарта C++, но пока из разрабатывали появился новый стандарт C++, при котором они не особо то и нужны. Но какую-то небольшую аудиторию они занять смогли, да и ситуация с библиотеками C++ будет выправляться очень долго. Так что ситуация неоднозначная — посмотрим куда в итоге развернётся индустрия.
В стандарте С++ появился borrow checker?
_>Лично под мой вкус подходят оба языка. Изначально я думал, что точно со временем надо будет переходить на Rust, только подождать пока там более менее устаканятся инструменты и библиотеки (с этим кстати до сих пор проблема). Ну а пока ждал, уже в современном C++ начали появляться новинки, обходящие Rust.
Это какие?
_>На самом деле я наверняка знаю про плюсы Rust'а побольше тебя. Там довольно много серьёзных вкусняшек, в том числе и в сравнение с C++. Нюанс в том, что ты в данной дискусии упираешь именно на ту область, в которой у Rust в реальности плюсов нет — в реализации многопоточного кода.
А ничего что его создавали для решения этой задачи?
Более того успешно написали на rust код который не смогли на С++ из-за того что программа разлеталась на куски.
_>Вообще то уже был явно продемонстрирован один из примеров. Но он естественно не единственный.
От жеж лицемер. В С++ в том примере будет тоже самое.
_>Кроме показанного Иваном можно вспомнить и про бесконечные unwrap. И ещё есть набор нюансов связанных не с реализацией логики, а исключительно с обслуживанием BC.
Так ты покажи. Я когда на rust код писал, что-то ничего страшного не заметил.
_>Будет в любом языке. И толщина этого талмуда зависит исключительно от применяемой архитектуры многотпоточности, а не от выбранного языка. Скажем при использование модели акторов талмуд будет скорее всего самым тоненьким (в любых языка). При использование модели синхронизации разделяемых данных барьерами талмуд будет однозначно толще (в любых языках). А самый толстый будет наверное в случае применения раличных lock-free инструментов (опять же в любых языках).
Талмуд для evmap: Не делай refresh внутри транзакции чтения.
_>Переход мьютексов к акторам — это приблизительно тоже самое, что переход с языка C на язык Java в области управления памятью.
Более 99% проблем с мьтексами это забыли захватить мьютекс и полезли что-то делать с разделяемыми данными.
Удачи сделать это в rust.
А устроить дедлок как мы выяснили можно и на акторах.
_>Ты весьма забавно пишешь эти "потрясающие откровения" про проблемы с производительностью у акторов, прямо в ответ на мою цитату с упоминанием проблем производительности. ))) И да, тормознутость и дедлок — это принципиально разные проблемы.
Ты не понял. Там не просто тормоза. Там программа фактически зависает. Латентность улетает в космос. Грубо говоря ответ приходит не через обычные 100 миллисекунд, а через час.
_>Ты тут кстати действительно оказался прав. Я не внимательно посмотрел в прошлый раз: увидев в начале описание утверждение о том что это lock-free структура (и поверив в это), сразу пошёл в конец смотреть графики и т.п. И пропустил важнейший момент в середине текста. Оказывается эти умники предлагают в случае нескольких писателей в эту, как бы "lock-free" сущность, использовать мьютекс! Такого я точно не ожидал... Такая детская хрень не то что любые гарантии акторов выкинет, оно ещё и производительность убьёт (ради которой и страдают с неудобными lock-free сущностями).
А ты точно графики смотрел?
Линейный рост производительности чтения видел?
Да и запись не сильно уступает конкурентному словарю. А вот на чтении оно намного быстрее.
А на счёт гарантий тут всё просто. Делаем пишущий актор и кучу читающих. Всё будет прекрасно работать.
И вообще по жизни задачи, где нужно больше одного писателя почти не встречаются.
_>Возможно. Надо смотреть конкретную реализацию. В любом случае целевые гарантии будут следствием применения данной модели, а не самого языка.
Без языка гарантий не будет.
_>>>таким образом, применяя модель акторов, мы получаем замену сложнейшей задачи на очень простую (которую элементарно можно отследить в коде приложения). WH>>Только в твоих мечтах. WH>>Особенно если на С++. _>Да элементарно даже на C + WinAPI. )))
Без гарантий.
_>С учётом того, что оказалось что evmap — это не lock-free, а требующая мьютекса хрень, думаю даже нет смысла обсуждать это. Т.е. да, некорректный код там не получишь — получишь тупо тормоза в лучше случае и дедлок в худшем.
На чтение lock-free. Да и больше одного писателя обычно не нужно. Так что и на запись lock-free.
А иметь lock-free чтение с уровнем изоляции serializable в очень многих задачах очень круто.
Собственно почти все read-write lock’и можно заменить на evmap. Тем более что эту систему можно сделать вокруг любого объекта, а не только словаря.
_>Только вот BC принуждает их использовать даже в тех случаях, когда они по факту не нужны. Т.е. в C++ в этом коде будет просто переменная, а в Rust'е придётся мутить обёртку ради удовлетворения BC.
Что за хрень ты несёшь? В каком месте Option<Box<Data>> упоминается BC? Ты вообще про что? И заметь ты сам так ничего и не показал.
Ну и в С++ вместо Box будет один из многих смартпоинтеров, а nullable типы это в любом случае зло. Так что Option тоже нужен без вариантов.
_>Ты же вот слился показать аналог простейшего C++ кода из нескольких строчек. Случаем не по той причине, что тебя BC послал с прямым аналогом? )))
Это простейший говнокод. Любители динамической типизации тоже такой фигнёй страдают. Напишут кусок говнокода, который на языке со статической типизаций не написать и объявляют победу.
А на вопрос когда такой код может понадобиться, сразу сливаются. Прямо как ты.
_>Какой у тебя интересный опыт разработки многопоточных решений. Даже интересно было бы посмотреть на какие-то конкретные реализации... )))
Ты же про свой опыт вообще ничего не говоришь.
Только с умным видом надуваешь щёки.
Но так и не показал, в какой задаче под одним мьтексом могут жить данные с разным временем жизни.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, FR, Вы писали:
_>>Пока что ты слился с реализацией простейшего кода в пару строчек на C++ (который Rust в теории должен заменять), причём в котором не было никаких низкоуровневых игр с указателями и т.п. опасных вещей. Однако пытаешься вилять, чтобы замять эту проблему. FR>Одна опасная вещь там все-таки есть поток может пережить функцию main, и получим доступ к убитым локальным переменным. В Rust как раз поэтому и не соберется.
Ну это у меня совершенно не специально получилась ещё одна мелкая побочная демонстрация неудобности Rust'а (потому как в том конкретном примере такой подход полностью оправдан).
Но конечно же ключевой смысл был совсем в другом — если заменить в моём коде локальную переменную на выделенную в куче скажем с помощью shared_ptr (а на Rust'е использовать Arc), то ситуация абсолютно не изменится. Мой код как работал, так продолжил бы. А в написание идеоматического кода на Rust по прежнему была бы большая проблема, несмотря на отсутствие каких-либо объективных причин.