Здравствуйте, alex_public, Вы писали:
_>>>Это на самом деле тоже иллюзия, даже без unsafe. Потому как гонки могут быть и в рамках одного потока (сопрограммы, лёгкие потоки и т.п.), а Rust как я понимаю это даже не пытается отслеживать. _>·>Ты не понимаешь термины. Иван говорит об отсутствии _гонок данных_ (data race), а ты говоришь о гонках вообще. _>То, что я описал, — это классические гонки данных, так что не надо тут глупости писать. Или тебе конкретный код показать что ли? )))
Ты описал какое-то своё понимание. Я тебе привёл цитату того, что под этим понимает, например, стандарт Плюсов. То что ты описал это называется race condition.
Код показывай, конечно.
_>·>
_>·>The memory model defined in the C11 and C++11 standards uses the term "data race" for a race condition caused by potentially concurrent operations on a shared memory location, of which at least one is a write. A C or C++ program containing a data race has undefined behavior.
_>·>
_>·>Так вот. В C++ это undefined behaviour, в rust — ошибка компиляции. _>Детский сад какой-то. Ты вообще понимаешь в чём собственно проблема с параллельным доступом к одной памяти?
Да, понимаю.
_>В чём смысл атомарности и нужна ли она например для int'ов?
В случае конкурентного чтения и записи, например, атомарость говорит о том, что читатель увидит либо старое значение, до записи, либо новое, после записи и ничто другое. Да, атомарность нужна для интов.
_>Как это всё решает например CAS, который доступен в том же C++11 даже без ассемблера
Что конкретно "всё"?
_>и как это соотносится с твоей цитатой о неопределённом поведение?
Никак не соотносится. Atomic не создают data race и не обладают undefined behaviour.
_>Может всё же стоит понимать как оно внутри работает, а не рассуждать как о чёрном ящике?
Да, стоит.
_>Да, и Rust в этом смысле ничем не отличается от C++ и других системных языков: https://doc.rust-lang.org/nomicon/races.html — там вроде как есть и про Undefined Behavior и про то, что гарантий нет (причём речь не про unsafe).
"A data race has Undefined Behavior, and is therefore impossible to perform in Safe Rust." собственно что мы тут тебе и талдычим. В то же время "C or C++ program containing a data race has undefined behavior.". В этом и отличие Rust от C/C++. Об чём спор-то?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Иван Дубров, Вы писали:
_>>Но здесь речь немного не о том. Основной нюанс в том, что приведённая мною функция не является ошибочной (в том смысле, что её использование не обязательно приводит к созданию некорректного кода)! ИД>С точки зрения Rust, как экосистемы, этот код некорректен. ИД>Вот тебе пример CVE Rust: https://www.cvedetails.com/cve/CVE-2019-12083/ ИД>Этот CVE по сути означает возможность приведения ссылки к произвольному типу, не прибегая к безопасному интерфейсу. То есть просто возможность сделать, грубо говоря, C-style приведение указателя считается за уязвимость.
Хм, у тебя тут вышла весьма неоднозначная аргументация. С одной стороны ты говоришь о нетерпимости в сообществе Rust'а к такому коду, а с другой сам же показываешь его пример, причём не абы где, а в стандартной библиотеке языка... )))
_>>Т.е. она вполне имеет право на жизнь скажем в какой-нибудь отладочной библиотеке (например для того чтобы побродить по структурам памяти, не влезая в их код) и т.п. Просто при этом она ещё и снимает все гарантии, так что при неверном использование она позволит компилятору создать некорректный код. И различных вариаций на тему подобных функций может быть множество... ИД>Ну нет, так не нельзя (понятно, что физически тебя никто не остановит -- см. actix gate, хотя и там вроде всё счастливо закончилось). Нужна подобная отладочная функция -- делай её unsafe, с комментарием какие гарантии ты перекладываешь на разработчика. ИД>Это один из ключевых механизмов обеспечения безопасности в Rust -- safe интерфейс не должен позволять использование освобождённой памяти, висящие ссылки, гонки данных и прочий undefined behavior.
Я это всё понимаю. Но думаю и ты понимаешь, что и реальный код и даже реальные библиотеки не всегда пишут "мастера архитектуры". ) Я сам конечно не стал бы писать такой код, который приводил тут как пример. Но кто-то может и написать, а язык никаких гарантий защиты от подобного не даёт...
И да, я на самом деле не считаю это каким-то страшным недостатком. Просто данный факт надо понимать и признавать, а не быть "упёртым фанатом", как некоторые наши коллеги. )
Здравствуйте, Marty, Вы писали:
WH>>Собственно rust по тому и возник что ребята из мозила хотели писать очень параллельный код но на С++ не получалось. Программа разлеталась на куски. А на rust получилось. M>Что, Мозиллу уже на расте переписали?
Частично. Расчёт CSS переписали. Уверен со временем перепишут и другие куски.
Сам понимаешь, что мозила большая. Быстро не переписать.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, Marty, Вы писали:
M>Ну и где написанный на нём софт? Кроме Paint.NET ничего хорошего я не видел
Например Unity.
Ну и просто мегатонны корпоративного софта.
M>Расту 8 лет уже, как никак. На шарпе в этом возрасте уже во всю куячили в корпоративном секторе
Большую часть этого времени раст был в нестабильном состоянии и постоянно менялся. Включая идеологию.
Изначально делали статически типизированный эрланг с толстым рантаймом. Но со временем он мутировал в zero-overhead язык системного программирования без рантайма. На расте теперь можно писать код для микроконтроллеров, где несколько килобайт памяти на всё.
Раст только недавно стабилизировали.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, alex_public, Вы писали:
WH>>Я увидел там unsafe без создания safe интерфейса. WH>>Нормальные люди так не делают. _>А можно узнать, где есть формулировка этого самого safe интерфейса? Т.е. уже понятно, что на уровне компилятора данного определения не существует и проверять он его не умеет. Но возможно есть какой-то официальный талмудик, в котором будут определения? )))
Именно на уровне языка это определение и существует.
И если тебе нужно написать unsafe, то ты должен соблюсти все обещания что ты сделал в сигнатуре функции.
_>>>Что такое "дамп зависшего приложения" и в каких условиях твои приложения выдают эту сущность пользователю? WH>>Зависит от того что за приложение. _>Да хоть один любой пример приведи)))
google: process dump
WH>>У меня есть чёткое понимание того какие классы ошибок устраняются и как именно это происходит. _>Вооот! Это уже деловой разговор. И у меня тоже есть чёткое понимание этого вопроса в Rust'а — я вполне согласен, что есть целые классы ошибок, от которых он защищает. Но вот в области многопоточности Rust точно не способен дать гарантии корректности.
Как мы выяснили, раст ты не знаешь. А значит делать выводы о том, что он может, не можешь.
Вот это конкретный залёт:
Ну сделай поиск, сколько unwrap'ов в твоём проекте? И если что, все эти вызовы являются чистым синтаксическим мусором, существующим исключительно для правильной работы BC.
_>Что-то я не понял с чем ты тут споришь собственно говоря. Может просто из принципа, что нельзя соглашаться, а надо обязательно хоть в чём-то возразить? ))) В данном примере мьютекс используется грубо говоря для отсылки сообщения из одного потока в другой (это собственно говоря основной предназначение таких переменных, причём чаще всего уведомляют не один поток, а несколько), а не для разделения каких-то рабочих данных между несколькими потоками.
Ох. Conditional variable используют для того чтобы сказать что данные которые защищает мьютекс изменились.
_>Ну и это естественно не единственное применение мьютексов в области синхронизации, не укладывающееся в схему "охрана куска данных".
Event это мьтекс + CV вокруг булевой переменной. Так что и тут защита куска данных.
_>Нет, они работают потому, что часто мьютекс может быть просто свободен (ждать не надо вообще) и соответственно если сразу уходить в ядро, то переключение контекста будет сделано впустую. Если же вызывать этот код только при занятом мьютексе, то он вполне оправдан, т.к. переключать контекст всё равно надо.
Что такое spinlock и почему их используют не знаешь.
_>Требования на упорядоченность сообщений в модель акторов не входит, так что никто на неё и не рассчитывает.
Но это гонки по определению.
_>И каким образом компилятор поймёт какие участки кода возможны для "параллельного" исполнения? ) В реальности это может быть произвольный код грубо говоря между двумя вызовами "await" (который кстати может быть ещё и не только явный, но и внутри других функций).
Иди читай про Sync, Send и borrow checker. Ибо если бы ты знал, как это работает, то не задавал бы глупых вопросов.
_>Так если код дальше будет банально требовать на входе переменную какого-то фиксированного типа, а ты подсунешь другой тип (пускай и с аналогичными методами), то ты думаешь этот код заработает? Это возможно только если весь дальнейший код написан в стиле шаблонов, чего тебе никто не обещал (и вообще редко бывает в небиблиотечном коде).
Мы вроде говорим про свой код, который контролируем. А значит, можем спокойно изменить сигнатуру.
Ну и главное ты так и не сказал, зачем нужен этот говнокод.
_>Эээ что? Какой ещё тип для разделения между потоками? Ты похоже уже привык к Rust'у и не можешь мыслить по другому (хотя как-то быстро это у тебя произошло, после Nemerle). В нормальном языке нам не надо плодить специальный тип для разделения данных — мы просто работаем с существующими переменными самых обычных рядовых типов.
Я привык не писать говнокод.
И когда хоть немного проектируешь систему, такое получается на любом языке.
_>>>Но вот ситуация с тем, что потокам могут понадобиться данные с разным временем жизни — это абсолютно нормальное и повседневное явление. WH>>Раз это явление повседневное то ты должен легко дать ответ на этот вопрос. WH>>Но ты уже несколько сообщений вертишься. WH>>Почему? WH>>Может по тому, что ты ради спора насосал говнокод из пальца, а реальных примеров в твоей практике никогда не было? _>На какой вопрос то?
Когда нужны данные с разным временем жизни под одним мьтексом?
Ты говоришь это явление повседневное.
Но ни ты. Ни другие люди так и не показали жизненный пример, когда такое нужно.
_>Ха, BC в данной реализации мьютексов служит исключительно для предотвращения сохранения ссылок (хотя указатель сохранить никто не мешает, как мы видели) на данные под мьютексом. А для собственно реализации подобного мьютекса достаточно языка с наличием дженериков и RAII. Таких языков в данный момент не мало, но что-то не припомню именно такой реализации мьютекса где-то ещё.
Без BC от такой реализации нет никакого толку.
Ну и главное ты не можешь показать жизненный пример, который не сводится к защите одного куска данных.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Т.е. под native ты подразумевал всего лишь FFI (т.е. в контексте Java JNI)?
И какое это имеет отношение к программированию на языке Java? ))) А то такими темпами можно начать утверждать, что аналитику на Питоне надо обязательно знать тонкости управления памятью процессором, ведь из Питона же можно вызвать код на Ассемблере!
_>>Откуда такие фантазии? ))) ·>Откуда такое агрессивное невежество?
Пока что от тебя наблюдается даже не невежество (я подозреваю что ты в курсе бредовости своих утверждений выше), а надежда на то, что тут на форуме сидят одни невежды, которые не поймут что ты несёшь откровенную чушь, чтобы как-то обосновать свои изначальные тезисы.
_>>Ммм, а где ты в тексте этой моей аналогии увидел хоть слово про какие-то гарантии? Ты мне предлагаешь поспорить с голосами в твоей голове? ))) ·>Что ты хотел показать этой аналогией?
Ситуацию в индустрии в области многотопочности. Т.к. ситуацию с управлением памятью (разделение на сборщик мусора/ручную/автоматическую, их плюсы и минусы) все более менее понимают и на аналогии с ней можно довольно просто показать ситуацию с многопоточностью, которая гораздо мутнее и многим вообще не известна (что и подтверждает данная дискуссия).
_>>·>А &-ссылки в C++ тоже запрещать надо для этого или как? _>>Ссылки C++ никак не относятся к механизму управления памятью — они её не выделяют и не освобождают. ·>Зато они позволяют на ровном месте обратиться к освобождённой памяти.
Только при их неверном использование. И я естественно говорю не о внимательности программиста, проверяющего руками каждое использование, а об общем концептуальном подходе (который кстати можно и как правило статического анализатора задать) по применению данного инструмента.
_>>·>Как вообще можно по куску кода понять — используется ли в данном коде "правильное управление памятью" или не очень? _>>Это ты про какой язык спрашиваешь, про Rust или C++ или ещё какой? ) ·>В данном месте, вроде бы очевидно, я говорил о С++.
Конечно можно. Например в современном C++ коде должно быть практические исключено (за редкими исключениями, типа использования unsafe в Rust) использование явных new и delete.
_>>>>В управление многопоточностью подобного решения пока к сожалению не существует! _>>·>Суть в том, что rust _компилятор_ проверяет код и чётко гарантирует отсутствие data race. Иными словами, даёт safety guarantees от data race. Если проверку отключить явно (т.е. использовать unsafe), то понятное дело, не проверяет, и гарантии не даёт. КО. _>>Во-первых подход Rust'а к многопоточности абсолютно не автоматический. Т.е. если для управления памятью умные указатели unique_ptr/box инкапсулируют в себя выделения и освобождение памяти, то в области параллелизма раскидывать по коду мьютексы и вызывать их захват надо по прежнему руками. Т.е. уже нельзя говорить ни о каком автоматизме. ·>Ну и не говори об автоматизме. Кто ж тебя заставляет?
Вообще то в моей аналогии речь как раз про автоматизм и была. ))) Как в управление памятью, так и во многопоточности. И ты попытался утверждать, что Rust умеет автоматическое управление в области многопоточности. А когда я тебе показал что не имеет, ты говоришь "Ну и не говори об автоматизме. Кто ж тебя заставляет?"?
_>>Во-вторых, одобренная компилятором программа, не гарантирует даже отсутствие гонок данных (не говоря уже о более сложных вещах — см. следующий пункт). И я здесь говорю даже не о явном использование unsafe, а о том что при отсутствие должного умения даже вполне корректные сущности (типа того же Atomic), которые компилятор спокойно допускает до многопоточного использования без всяких unsafe, способны наделать бед. ·>Гарантирует. Atomic не создаёт гонки данных. Data race даёт неопределённое поведение, т.е. нельзя ничего утверждать о поведении такой программы. Поведение atomic строго определено и можно просчитать возможные исходы что может получиться в результате.
Atomic — это не какая-то магия, а точно такая обычная область разделяемой памяти (так что внутри Atomic матёрый unsafe, если что). Единственная разница в том, что мы резко ограничиваем набор операций для работы с этой памятью (реализуя их все через CAS, что сводится к специфической инструкции процессора). В Rust это реализовано через соответствующий класс, который внутри с помощью unsafe играется с разделяемой памятью, и при этом говорит компилятору, что его функции можно спокойно вызывать из разных потоков.
Так вот, даже реализация Atomic из стандартной библиотеки даёт доступ не только к простейшим гарантированным операциям типа fetch_add, но и к базовой функциональности CAS, которая при неаккуратном использование способна привести к потере данных. Ну а если этого недостаточно, то мы легко можно реализовать свой (более продвинутый чем-то) Atomic, который компилятор будет спокойно воспринимать и в котором при этом будут ещё более уязвимые места.
И да, во всём этом нет ничего страшного, с точки зрения большинства современных языков программирования (ну не считая построенных вокруг акторов, типа Эрланга) — это нормальная ситуация. Только вот она не укладывается в некоторые мифы о неких гарантиях... )))
_>>Ну и в третьих (хотя это пожалуй самое главное на практике) — даже если мы добьёмся отсутствия гонок с помощью расстановки мьютексов по коду, это ещё не гарантирует нам корректный многопоточный код. ·>И что?
На самом деле ничего страшного, конечно же при условии что ты не упёртый фанатик, верящий в миф типа "если компилятор Rust одобрит код без unsafe, то значит в нём точно всё в порядке с многопоточностю".
_>>Какие-то гарантии могут дать только какие-то архитектурные приёмы, регламентирующие использование этих мьютексов. Но компилятор подобное отследить не в состояние. ·>Каким образом эти гарантии будут обеспечиваться?
В большинстве языков административно. В редких случаях (типа Эрланга) это может быть упаковано на уровне рантайма языка, но у этого всегда есть соответствующая цена.
_>>Только пока что Rust не может скомпилировать и корректный вариант. В том смысле что никто из защитников языка не смог представить соответствующий код. Может ты сможешь? ))) ·>Ты таки задачу так и не обозначил. А переводить некий говнокод с одного яп на другой — бессмысленное занятие.
В общем случае — возможно. Но в данном случае это языки одного класса, для одной ниши и второй язык рассматривают как потенциальную замену первому. Поэтому в данном случае вопрос "как мне переписать данный работающий код с первого языка на второй" является вполне корректным.
_>>Ну ты же понимаешь, что это означает как минимум "Rust не гарантирует отсутствие утечек памяти и других ресурсов, управляемых через RAII"? ·>Ну да. И?
Просто получается что в данном моменте "как бы гарантированный" Rust на практике имеет меньше гарантий, чем большинство других языков с RAII (включая тот же самый C++).
_>>·>А что же тогда будет гарантией "безопасной многопоточности"? Что вообще значит "безопасная многопоточность" по-твоему? В каких системах в принципе есть безопасность? _>>Желательно получить такой инструмент, который брал бы на себя заботу обо всех технических нюансах, оставляя нам заботу только об ошибках в бизнес-логике. Из существующих подходов к этому ближе всего модель акторов. Но она к сожалению имеет существенные накладные расходы. ·>В чём выражается безопасность акторов-то? Что значит "безопасная многопоточность"-то?
Я вроде как ясно написал всё. Ну повторюсь ещё раз, вдруг поможет... Идеально безопасной автоматической многопоточности (так же как управления памятью и всего остального) естественно не существует, просто в силу потенциального наличия бизнес-ошибок в коде. Но если мы предположим, что имеем гарантированно безопасный с точки зрения бизнес-логики код (скажем на неком псевдоязыке или вообще диаграммами), то хотелось бы чтобы при его переводе в код уже на нашем реальном рабочем языке, не возникло никаких дополнительных ошибок. В том смысле что они не возникли бы не из-за внимательности программиста, а из-за принципиальной невозможности их возникновения.
Здравствуйте, ·, Вы писали:
_>>То, что я описал, — это классические гонки данных, так что не надо тут глупости писать. Или тебе конкретный код показать что ли? ))) ·>Ты описал какое-то своё понимание. Я тебе привёл цитату того, что под этим понимает, например, стандарт Плюсов. То что ты описал это называется race condition. ·>Код показывай, конечно.
Возьмём такой простейший синхронный код:
int global_var=0;
void Test(int param)
{
global_var=process(global_var, param);//process - длительная блокирующая операция
}
Test(10);
Test(20);
В его корректной работе нет никаких внутренних сложностей — это классический однопоточный код, в котором можно вообще не вспоминать про какие-то гонки и т.п. Предположим, что мы захотели перевести работу этого кода на асинхронный интерфейс (да, здесь в этом нет никакого смысла, но сейчас такая мода на эту тему в индустрии, что зачем-то переводят всё подряд, даже не включая мозг) и потом линеаризовать эту асинхронность с помощью сопрограмм. На практике это будет выглядеть как-то так:
int global_var=0;
void Test(int param)
{
global_var=await process_asyc(global_var, param);//process_asyc - длительная асинхронная операция
}
Test(10);
Test(20);
...
MainThreadLoop();
Так вот, надеюсь не надо пояснять, почему результат работы этого теперь уже некорректного кода будет зависеть от случайных факторов? При этом вся работа с проблемной переменной будет происходить исключительно в рамках одного потока...
_>>·>
_>>·>The memory model defined in the C11 and C++11 standards uses the term "data race" for a race condition caused by potentially concurrent operations on a shared memory location, of which at least one is a write. A C or C++ program containing a data race has undefined behavior.
_>>·>
_>>·>Так вот. В C++ это undefined behaviour, в rust — ошибка компиляции. _>>Детский сад какой-то. Ты вообще понимаешь в чём собственно проблема с параллельным доступом к одной памяти? ·>Да, понимаю.
Судя по всему не особо. Иначе бы ты сразу отделил работу с атомарными данными от других. Причём второе можно свести к первому, если грубо говоря каждый раз выделять данные в куче и работать с указателем, а не самими данными. Ну а если такое не подходит, то требуются уже блокировки или потеряется согласованность данных (один из примеров того самого undefined behavior).
_>>В чём смысл атомарности и нужна ли она например для int'ов? ·>В случае конкурентного чтения и записи, например, атомарость говорит о том, что читатель увидит либо старое значение, до записи, либо новое, после записи и ничто другое. Да, атомарность нужна для интов.
Правильно. Только вот ассемблерная инструкция перемещения регистра в память (что равносильно оператору равенства в твоём языке программирования) является вполне себе атомарной, так что для int'ов(подразумевая под ним машинное слово) оно и так автоматически имеется. Поэтому для ограниченного числа задач на самом деле можно параллельно обращаться к одному int'у и всё будет нормально. Проблема возникает если надо использовать корректное предыдущее значение — тогда на помощь приходит CAS.
_>>Как это всё решает например CAS, который доступен в том же C++11 даже без ассемблера и как это соотносится с твоей цитатой о неопределённом поведение? ·>Никак не соотносится. Atomic не создают data race и не обладают undefined behaviour.
Да, но это же случай одновременного доступа на запись к некой общей памяти и по твоей цитате выше оно обязательно должно быть undefined behaviour в C++!
_>>Да, и Rust в этом смысле ничем не отличается от C++ и других системных языков: https://doc.rust-lang.org/nomicon/races.html — там вроде как есть и про Undefined Behavior и про то, что гарантий нет (причём речь не про unsafe). ·>"A data race has Undefined Behavior, and is therefore impossible to perform in Safe Rust." собственно что мы тут тебе и талдычим. В то же время "C or C++ program containing a data race has undefined behavior.". В этом и отличие Rust от C/C++. Об чём спор-то?
Ну может стоило приводить цитату целиком, а не обрезать на самом интересном? ))) Целиком оно выглядит так:
A data race has Undefined Behavior, and is therefore impossible to perform in Safe Rust. Data races are mostly prevented through Rust's ownership system: it's impossible to alias a mutable reference, so it's impossible to perform a data race. Interior mutability makes this more complicated, which is largely why we have the Send and Sync traits (see below).
Так что там про гарантии? )))
По факту настоящие гарантии можно было бы дать и в Rust, но это очень серьёзно ограничило бы язык. Они выбрали другой путь (и правильно!), на котором гарантий нет.
Кстати, по этой ссылке дальше идёт ещё более интересный текст про многопоточность в Rust в общем. Что никаких гарантий естетвенно нет и единственно чем реально озабочем тут Rust, это сохранение memory safety — как я собственно и говорил изначально.
Здравствуйте, alex_public, Вы писали:
_>>>Что за unsafe в Java? _>·>sun.misc.Unsafe, jdk.internal.misc.Unsafe. _>Ну если оно есть в Java, то ты конечно же сможешь показать мне описание этого API в официальной документации Java, да? )))
Причём тут официальная документация?
_>>>Что за native? _>·>https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.3.4 _>Т.е. под native ты подразумевал всего лишь FFI (т.е. в контексте Java JNI)?
Ничего я не подразумевал, а говорил прямо.
_>И какое это имеет отношение к программированию на языке Java? ))) А то такими темпами можно начать утверждать, что аналитику на Питоне надо обязательно знать тонкости управления памятью процессором, ведь из Питона же можно вызвать код на Ассемблере!
Так это следствия из твоих утверждений, вот и бред.
_>>>Ммм, а где ты в тексте этой моей аналогии увидел хоть слово про какие-то гарантии? Ты мне предлагаешь поспорить с голосами в твоей голове? ))) _>·>Что ты хотел показать этой аналогией? _>Ситуацию в индустрии в области многотопочности. Т.к. ситуацию с управлением памятью (разделение на сборщик мусора/ручную/автоматическую, их плюсы и минусы) все более менее понимают и на аналогии с ней можно довольно просто показать ситуацию с многопоточностью, которая гораздо мутнее и многим вообще не известна (что и подтверждает данная дискуссия).
Конечно, подтверждает. Правда судя по дискуссии, не "многим", а тебе конкретно.
_>>>Ссылки C++ никак не относятся к механизму управления памятью — они её не выделяют и не освобождают. _>·>Зато они позволяют на ровном месте обратиться к освобождённой памяти. _>Только при их неверном использование.
Сформулируй "неверное использование".
_>И я естественно говорю не о внимательности программиста, проверяющего руками каждое использование, а об общем концептуальном подходе (который кстати можно и как правило статического анализатора задать)
Нельзя задать.
_>по применению данного инструмента.
Это опять какой-то бред. Концептуальный подход — это всё бла-бла. Вот тебе моя гениальная концепция: "Пишите программы без багов". Вот я изобрёл абсолютно безопасный язык программирования. Осталось сделать официальную документацию и готово, всем твоим критериям гарантий безопасности удовлетворяет. Нобелевку мне, можно две.
_>>>·>Как вообще можно по куску кода понять — используется ли в данном коде "правильное управление памятью" или не очень? _>>>Это ты про какой язык спрашиваешь, про Rust или C++ или ещё какой? ) _>·>В данном месте, вроде бы очевидно, я говорил о С++. _>Конечно можно.
ИЧСХ ещё никому не удалось, кроме тебя.
_>Например в современном C++ коде должно быть практические исключено (за редкими исключениями, типа использования unsafe в Rust) использование явных new и delete.
И что? Это избавит, скажем, от утечек, вызваных циклическими зависимостями? Или тупо от обращений к невалидной памяти?
_>>>Во-первых подход Rust'а к многопоточности абсолютно не автоматический. Т.е. если для управления памятью умные указатели unique_ptr/box инкапсулируют в себя выделения и освобождение памяти, то в области параллелизма раскидывать по коду мьютексы и вызывать их захват надо по прежнему руками. Т.е. уже нельзя говорить ни о каком автоматизме. _>·>Ну и не говори об автоматизме. Кто ж тебя заставляет? _>Вообще то в моей аналогии речь как раз про автоматизм и была. ))) Как в управление памятью, так и во многопоточности. И ты попытался утверждать, что Rust умеет автоматическое управление в области многопоточности. А когда я тебе показал что не имеет, ты говоришь "Ну и не говори об автоматизме. Кто ж тебя заставляет?"?
Нет, я не пытался утверждать такое. Или цитату в студию.
_>·>Гарантирует. Atomic не создаёт гонки данных. Data race даёт неопределённое поведение, т.е. нельзя ничего утверждать о поведении такой программы. Поведение atomic строго определено и можно просчитать возможные исходы что может получиться в результате. _>Atomic — это не какая-то магия, а точно такая обычная область разделяемой памяти (так что внутри Atomic матёрый unsafe, если что). Единственная разница в том, что мы резко ограничиваем набор операций для работы с этой памятью (реализуя их все через CAS, что сводится к специфической инструкции процессора). В Rust это реализовано через соответствующий класс, который внутри с помощью unsafe играется с разделяемой памятью, и при этом говорит компилятору, что его функции можно спокойно вызывать из разных потоков.
_>Так вот, даже реализация Atomic из стандартной библиотеки даёт доступ не только к простейшим гарантированным операциям типа fetch_add, но и к базовой функциональности CAS, которая при неаккуратном использование способна привести к потере данных. Ну а если этого недостаточно, то мы легко можно реализовать свой (более продвинутый чем-то) Atomic, который компилятор будет спокойно воспринимать и в котором при этом будут ещё более уязвимые места.
Кул стори, бро. Тебе осталось понять и рассказать какое это всё отношение имеет к data race.
_>>>Ну и в третьих (хотя это пожалуй самое главное на практике) — даже если мы добьёмся отсутствия гонок с помощью расстановки мьютексов по коду, это ещё не гарантирует нам корректный многопоточный код. _>·>И что? _>На самом деле ничего страшного, конечно же при условии что ты не упёртый фанатик, верящий в миф типа "если компилятор Rust одобрит код без unsafe, то значит в нём точно всё в порядке с многопоточностю".
Пока этот бред я слышал только от тебя. Ты упёртый фанатик?
_>·>Каким образом эти гарантии будут обеспечиваться? _>В большинстве языков административно. В редких случаях (типа Эрланга) это может быть упаковано на уровне рантайма языка, но у этого всегда есть соответствующая цена.
Верно. В большинстве языков — административно, а в rust — они обеспечиваются компилятором. В этом и отличие.
_>·>Ты таки задачу так и не обозначил. А переводить некий говнокод с одного яп на другой — бессмысленное занятие. _>В общем случае — возможно. Но в данном случае это языки одного класса, для одной ниши и второй язык рассматривают как потенциальную замену первому. Поэтому в данном случае вопрос "как мне переписать данный работающий код с первого языка на второй" является вполне корректным.
Вот тебе:
Работающий код, делающий ровно то же что и твой. Доволен?
_>>>Ну ты же понимаешь, что это означает как минимум "Rust не гарантирует отсутствие утечек памяти и других ресурсов, управляемых через RAII"? _>·>Ну да. И? _>Просто получается что в данном моменте "как бы гарантированный" Rust на практике имеет меньше гарантий, чем большинство других языков с RAII (включая тот же самый C++).
Каким образом C++ обеспечивает гарантии отсутствия утечек памяти?
_>>>Желательно получить такой инструмент, который брал бы на себя заботу обо всех технических нюансах, оставляя нам заботу только об ошибках в бизнес-логике. Из существующих подходов к этому ближе всего модель акторов. Но она к сожалению имеет существенные накладные расходы. _>·>В чём выражается безопасность акторов-то? Что значит "безопасная многопоточность"-то? _>Я вроде как ясно написал всё. Ну повторюсь ещё раз, вдруг поможет... Идеально безопасной автоматической многопоточности (так же как управления памятью и всего остального) естественно не существует, просто в силу потенциального наличия бизнес-ошибок в коде. Но если мы предположим, что имеем гарантированно безопасный с точки зрения бизнес-логики код (скажем на неком псевдоязыке или вообще диаграммами), то хотелось бы чтобы при его переводе в код уже на нашем реальном рабочем языке, не возникло никаких дополнительных ошибок. В том смысле что они не возникли бы не из-за внимательности программиста, а из-за принципиальной невозможности их возникновения.
Это всё идеал. А конкретика? Что конкретно дают акторы? "точно всё в порядке с многопоточностю"?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, alex_public, Вы писали:
_> global_var=await process_asyc(global_var, param);//process_asyc — длительная асинхронная операция _>Так вот, надеюсь не надо пояснять, почему результат работы этого теперь уже некорректного кода будет зависеть от случайных факторов? При этом вся работа с проблемной переменной будет происходить исключительно в рамках одного потока...
Ок, хороший пример, давай я помогу тебе разобраться. В случае однопоточного асинхронного исполнения будет race condition, и в итоге результат выполнения будет недетерминирован, но не являться undefined behaviour. Недерминированность выражается в том, что значение global_var будет одним из значений process, но неясно каким из.
Если это же сделать многопоточным, т.е. эту global_var будут пытаться писать одновременно разные потоки без всякой синхронизации, то возможен data race и значение global_var может быть каким угодно.
Принципиальная разница в том, что в случае недетерминизма мы можем анализировать код и делать какие-то заключения (reasoning) о поведении данной части кода и программы в целом, то в случае наличия UB — мы можем сказать лишь одно: behavior of the program is undefined.
Например, допустим мы знаем, что process возвращает только положительные числа.
В случае недетерминизма, мы можем смело утверждать, что global_var будет положительным числом.
В случае же undefined behaviour — в переменной может оказаться всё что угодно, никаких гарантий нет. Притом этот результат может зависеть от версии компилятора, от железа, от операционки, от погоды на марсе и т.п.
Кстати, если присвоение global_var будет атомарным, то опять получится недетерминизм, а не undefined behaviour.
_>>>Детский сад какой-то. Ты вообще понимаешь в чём собственно проблема с параллельным доступом к одной памяти? _>·>Да, понимаю. _>Судя по всему не особо. Иначе бы ты сразу отделил работу с атомарными данными от других. Причём второе можно свести к первому, если грубо говоря каждый раз выделять данные в куче и работать с указателем, а не самими данными. Ну а если такое не подходит, то требуются уже блокировки или потеряется согласованность данных (один из примеров того самого undefined behavior).
С чего это я должен был что-то там отедлять? Атомарные данные ты принёс в дискуссию сам, сам с ними и разбирайся. Если есть вопросы — задавай.
_>·>В случае конкурентного чтения и записи, например, атомарость говорит о том, что читатель увидит либо старое значение, до записи, либо новое, после записи и ничто другое. Да, атомарность нужна для интов. _>Правильно. Только вот ассемблерная инструкция перемещения регистра в память (что равносильно оператору равенства в твоём языке программирования) является вполне себе атомарной,
Для какого CPU? А про membar-ы не забыл?
_>так что для int'ов(подразумевая под ним машинное слово) оно и так автоматически имеется.
Если бы С++/Rust/etc стандарт писался ровно для одного CPU и т.п., то возможно твоё утверждение было бы и верным.
_>Поэтому для ограниченного числа задач на самом деле можно параллельно обращаться к одному int'у и всё будет нормально.
"А у меня всё работаииит!"
_>Проблема возникает если надо использовать корректное предыдущее значение — тогда на помощь приходит CAS.
А так же проблема возникает ещё в 100500 случаев, например, если этот конкретный int вдруг невыровненный оказался.
_>>>Как это всё решает например CAS, который доступен в том же C++11 даже без ассемблера и как это соотносится с твоей цитатой о неопределённом поведение? _>·>Никак не соотносится. Atomic не создают data race и не обладают undefined behaviour. _>Да, но это же случай одновременного доступа на запись к некой общей памяти и по твоей цитате выше оно обязательно должно быть undefined behaviour в C++!
Ну я не стал цитировать всё. Так и быть, раз ты не способен, я нагуглил для тебя точную формулировку:
A program that has two conflicting evaluations has a data race unless
* both evaluations execute on the same thread or in the same signal handler, or
* both conflicting evaluations are atomic operations (see std::atomic), or
* one of the conflicting evaluations happens-before another (see std::memory_order)
Кстати, "same thread" — как раз твой случай про однопоточные сопрограммы.
Читай далее, тут: https://en.cppreference.com/w/cpp/language/memory_model если ещё что-то непонятно.
_>Ну может стоило приводить цитату целиком, а не обрезать на самом интересном? ))) Целиком оно выглядит так: _>
_>A data race has Undefined Behavior, and is therefore impossible to perform in Safe Rust. Data races are mostly prevented through Rust's ownership system: it's impossible to alias a mutable reference, so it's impossible to perform a data race. Interior mutability makes this more complicated, which is largely why we have the Send and Sync traits (see below).
_>Так что там про гарантии? )))
Ну баги никто не отменял, поэтому и mostly. Плюс below это: "... Only in conjunction with some other unsafe code...".
_>По факту настоящие гарантии можно было бы дать и в Rust, но это очень серьёзно ограничило бы язык. Они выбрали другой путь (и правильно!), на котором гарантий нет.
Нет, они чётко описали что конкретно гарантируется и в каких случаях.
_>Кстати, по этой ссылке дальше идёт ещё более интересный текст про многопоточность в Rust в общем. Что никаких гарантий естетвенно нет и единственно чем реально озабочем тут Rust, это сохранение memory safety — как я собственно и говорил изначально.
Там про race condition.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
_>Ну может стоило приводить цитату целиком, а не обрезать на самом интересном? ))) Целиком оно выглядит так:
_>A data race has Undefined Behavior, and is therefore impossible to perform in Safe Rust. Data races are mostly prevented through Rust's ownership system: it's impossible to alias a mutable reference, so it's impossible to perform a data race. Interior mutability makes this more complicated, which is largely why we have the Send and Sync traits (see below).
_>Так что там про гарантии? )))
Ой. Перечитал повнимательнее. Гарантии в первом предложении. Impossible и точка. Смысл mostly тут совсем в другом.
Гонки данных в основном предовращены благодаря системе владения. Однако, этого не хватило, поэтому пришлось ещё ввести traits Send и Sync.
Т.е. гарантия отсутсвтия data race в Safe Rust обеспечивается совокупностью BC, Send и Sync.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
_>Так вот, надеюсь не надо пояснять, почему результат работы этого теперь уже некорректного кода будет зависеть от случайных факторов? При этом вся работа с проблемной переменной будет происходить исключительно в рамках одного потока...
Вот это залёт так залёт. await блокирует исполнение до того момента как process_asyc вернёт результат.
_>Правильно. Только вот ассемблерная инструкция перемещения регистра в память (что равносильно оператору равенства в твоём языке программирования) является вполне себе атомарной, так что для int'ов(подразумевая под ним машинное слово) оно и так автоматически имеется.
Ещё один залёт. Не на всех платформах такое работает.
_>•>Никак не соотносится. Atomic не создают data race и не обладают undefined behaviour. _>Да, но это же случай одновременного доступа на запись к некой общей памяти и по твоей цитате выше оно обязательно должно быть undefined behaviour в C++!
Опять залёт. Атомики производят синхронизацию. Так что тут нет никакого одновременного доступа.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, alex_public, Вы писали:
_>Хм, у тебя тут вышла весьма неоднозначная аргументация. С одной стороны ты говоришь о нетерпимости в сообществе Rust'а к такому коду, а с другой сам же показываешь его пример, причём не абы где, а в стандартной библиотеке языка... )))
Этот пример показывает серьёзное отношение к подобным вопросам и то, что ошибки делают все.
Здравствуйте, alex_public, Вы писали:
_>Эмм, зачем такие сложности? Обычная глобальная переменная и всё — Rust же спокойно с ними работает.
Я не особо силён в этих вопросах, но, по-моему, это всё идёт к LLVM aliasing-у и то, что "типа работает", на самом деле может быть UB. Со static mut тоже не всё так просто.
Сейчас, по-видимому, идёт работа по формализации unsafe: https://www.ralfj.de/blog/2019/01/12/rust-2019.html
_>Да, он ругается на доступ к ней из нескольких потоков без unsafe, но тут то у нас будет один поток, так что никаких претензий.
Даже в одном потоке доступ может быть UB, если берётся несколько ссылок на один static mut.
_>Единственно что я не в курсе есть ли сейчас в Rust'е библиотека сопрограмм (типа Boost.Coroutine2), чтобы можно было попробовать записать такой код. В самом языке сопрограмм почему-то (странно для вроде как современного языка — уже даже в древнем C++ их сейчас вводят на уровне языка, а не библиотеки) нет.
Сопрограммы -- stackless или stackful? Как я понимаю, stackful были в Rust в виде green threads, но их убрали из-за несоответствия философии Rust "zero-cost abstractions". Stackless coroutines могут быть построены на futures/async/await, но эта область сейчас только-только стандартизуется.
Впрочем, использовать futures можно уже довольно давно -- насколько я понял, dropbox что-то подобное делают в своём движке синхронизации -- они используют future как сопрограммы. Может быть даже ранний async/await используют.
Здравствуйте, WolfHound, Вы писали:
_>>Ещё раз: отсутствие проблем гарантируется при соблюдение некоторых условий. И смысл этой "математики" тут в том, что мы заменяем сложные и неочевидные условия, на достаточно простые. Правда, при этом слегка сужая область применения. WH>Так и в случае с мьютексами правило очень простое. WH>Нужно захватывать мьютексы в одном порядке. А учитывая, что в 99% случаев код больше одного мьютекса не захватывает то и проблем не возникает.
Вот что мне нравится в дискуссиях с фанатиками, так это их непробиваемые двойные стандарты. )))
Когда какой-нибудь программист на C будет рассказывать в точности аналогичные истории (про соблюдение простого правила и отсутствие проблем) насчёт управления памятью, ты будешь долго и демонстративно над ним смеяться. Но как только речь зашла об области, в которой у твоего языка аналогичная ситуация, так подобные заявления резко становятся абсолютно нормальными...
_>>Ну а некое подмножество BC, позволяющее безопасно управлять памятью появилось в стандарте языка C++11 и было доведено до ума в C++14. WH>Ты про что? Только не говори, что ты семантику перемещения называешь BC. Ибо это даже не рядом. Да и перемещение в С++ сделано хреново. В rust переменная после перемещения становится не доступной. А в С++ остаётся зомби объект.
Не совсем, хотя семантика перемещения крайне важна — она была завершающим штрихом к базису в языке, позволяющему реализовать подобные вещи (и многие другие, требующие работы с некопируемыми объектами). А речь была о введение в стандарт языка таких вещей как unique_ptr, shared_ptr/weak_ptr, и сопутствующая им инфраструктура.
_>>Например сопрограммы (на уровне языка, со всеми вытекающими из этого вкусняшками), WH>В rust это тоже скоро будет.
Странно что их не было изначально, всё же Rust вроде как современный язык, а уже отстаёт от старичка C++. Кстати, а какие они будут, stackful или stackless? У каждого из этих типов есть свои преимущества. В C++ уже много лет как доступны stackful сопрограммы через библиотеку Boost.Coroutine2. А сейчас stackless реализацию приняли в стандарт языка.
_>> контракты (которые на уровне языка — компилятор может использовать для оптимизации кода), WH>Какое отношение оно имеет к оптимизации?
Ты серьёзно не понимаешь? ))) В C++ ведь постоянно идёт inline кода, и если ты например используешь чужую функцию в которой есть ветвление в зависимости от значения параметра, а в твоей функции стоит контракт на значение этого параметра, определяющий "if", то в итоговом коде уже не будет ветвления. То же касается просто всяческих проверок, выборов типа алгоритма и т.п. Можешь ознакомиться здесь http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/p0147r0.html подробнее.
_>>вычисления на этапе компиляции (полноценный код с контейнерами, полиморфизмом и т.п.), WH>В rust есть макросы. Так что это всё детски сад.
Не не не, ты не путай. Если бы речь шла о метапрограммирование (в C++ оно на шаблонах, как ты знаешь), то я бы тут тоже много новинок в C++ мог вспомнить (типа введения концептов, строк в шаблонах и т.п.), но я в курсе что в Rust'е с этим всё хорошо и поэтому даже не заговаривал на эту тему. А здесь была речь совсем о другом: о constexpr. Это не метакод, а самый обычный код, состоящий из обычных функций, которые можно точно так же вызвать и из рантайма. Но при этом все вычисления производит компилятор в процессе сборки.
_>>И думаю что если бы этот кусочек переписали бы на современный C++, то результат был бы в точности такой же. WH>За счёт чего? BC нет. Реализуемых компилятором трейтов Sync и Send нет.
И это давно уже обсуждалось. Посмотри например это http://www.rsdn.org/forum/philosophy/6963027?tree=tree сообщение и остальные по ветке ниже.
_>>Тебе уже наглядно продемонстрировали, что не будет. WH>Мы говорим про Option<Box<Data>> на который ты возбудился. WH>Так что ещё как будет.
Это в твоих фантазиях мы говорим про это, причём я тебе уже раза два на это указал. Я же говорю о необходимости использовать контейнеры в Rust в тех случаях, когда в C++ достаточно обойтись обычной переменной. Более того, я тебе продемонстрировал конкретный пример кода, на котором ты слился.
Собственно в этой дискуссии только я и Иван говорили о чём-то аргументированно (с примерами кода), а остальные исключительно сливались при переходе к конкретике.
WH>>>Так ты покажи. Я когда на rust код писал, что-то ничего страшного не заметил. _>>Ну сделай поиск, сколько unwrap'ов в твоём проекте? WH>Ни одного.
_>>И если что, все эти вызовы являются чистым синтаксическим мусором, существующим исключительно для правильной работы BC. WH>Ты rust вообще не знаешь. WH>unwrap к BC вообще никакого отношения не имеет. WH>Он исключительно про обработку ошибок. Или точнее про игнорирование обработки ошибок. WH>Так что если ты видишь в коде unwrap это означает что код по-быстрому наговнокодили и потом нужно будет отдавать технический долг.
Я курсе про обработку ошибок. Более того, года 3 назад на этом форуме была большая дискуссия на эту тему, в которой я участвовал и в которой обсуждался в том числе и Rust. Но нюанс в том, что без лишнего контейнера, из которого надо попытаться вытащить данные, этого кода просто не было бы.
Кстати, в том же C++ механика сходная (с использованием unwrap), хотя поменьше синтаксического шума — традиционно тут просто используется переопределённый оператор *, который в случае ошибки кидает исключение. Однако в моём изначальном сообщение речь шла не об этих мелочах, а именно о лишних контейнерах.
_>>Что-то ты пропустил часть инструкции, написанной у них же на первой странице: используйте mutex для записи данных из нескольких потоков. Ну а к этой инструкции соответственно можно приложить хороший талмудик на тему безопасной работы с мьютексами. WH>1)В 99% случаях работать из множества потоков не нужно. WH>2)Не путай мьютексы в rust с мьютексами во всех остальных языках. В rust с мьютексом накосячить очень сложно.
Мы же уже обсудили, что мьютексы Rust'а никак не защищают от дедлоков и прочих ошибок многопоточности. Ты снова начинаешь? )))
_>>Ну и да, с моделью акторов эта хрень в любом случае не дружит, по целому ряду причин. WH>Назови хоть одну причину, почему она не будет работать с акторами в rust'е? BC передаёт привет.
Модель актров НАПРЯМУЮ ЗАПРЕЩАЕТ РАЗДЕЛЯЕМЫЕ МУТАБЕЛЬНЫЕ ДАННЫЕ. Так понятно? ))) Я уже молчу про потребность использования мьютексов, которая точно так же напрямую противоречит модели мьютексов. Но на фоне первого косяка это уже просто ерунда.
_>>Повторюсь наверное уже в 10-ый раз (странно, что до тебя не могут дойти такие очевидные истины): мы получаем гарантии корректной многопоточности, при условии соблюдения некоторых простейших правил в своём коде. В данном контексте это будет звучать как-то типа "не использовать указатель после его отправки в PostMessage" (ну это помимо общепринятого "не использовать глобальные переменные") и т.п. WH>А в случае с rust за этими правилами следит компилятор. И ты не сможешь их нарушить.
Вау, Rust имеет какие-то удобства по сравнению с C+WinAPI, да не может быть. )))
_>>Причём тут говнокод или нет? Это компилируемый, корректно работающий, и выдающий фиксированный результат код на языке со статической типизацией. Ты можешь повторить его на Rust или нет? Напрямую уже видно что нет, но давай обсудим какие модификации тебе необходимы, чтобы он всё же хотя бы собрался. WH>Необходимо знать какую задачу ты решаешь. WH>Пока что видно что ты написал говнокод только ради того чтобы написать говнокод. WH>Статическая типизация она вообще всегда отвергает некоторое множество корректных программ. И чем сильнее типизация, тем больше ошибок она ловит и больше корректных программ отвергает. WH>Вот только нужны ли эти программы? Ни ты, ни любители динамической типизации, которые используют ровно этот же аргумент пользы от всех этих выкрутасов показать не в состоянии.
Ну т.е. обсуждать что надо поправить в коде (например взять вместо локальной переменной, выделенную в куче) ты не хочешь, а предпочитаешь вместо конкретных технических вопросов продолжать отмазываться с помощью демагогии. Понятно всё с тобой.
Здравствуйте, WolfHound, Вы писали:
WH>>>Я увидел там unsafe без создания safe интерфейса. WH>>>Нормальные люди так не делают. _>>А можно узнать, где есть формулировка этого самого safe интерфейса? Т.е. уже понятно, что на уровне компилятора данного определения не существует и проверять он его не умеет. Но возможно есть какой-то официальный талмудик, в котором будут определения? ))) WH>Именно на уровне языка это определение и существует.
Да? А почему тогда компилятор не отследил его нарушение и не ругнулся?
WH>И если тебе нужно написать unsafe, то ты должен соблюсти все обещания что ты сделал в сигнатуре функции.
Ну и? Какие обещания сделаны в сигнатуре этой функции и почему они не соблюдены?
fn ptr2ref<T>(p: *const T)->&'static T
{
return unsafe {&*p};
}
_>>>>Что такое "дамп зависшего приложения" и в каких условиях твои приложения выдают эту сущность пользователю? WH>>>Зависит от того что за приложение. _>>Да хоть один любой пример приведи))) WH>google: process dump WH>
Т.е. ты предлагаешь пользователям зависших приложений устанавливать какие-то системные утилиты, делать дамп всего адресного пространства процесса и отсылать его производителю ПО? Я правильно тебя понял?
WH>>>У меня есть чёткое понимание того какие классы ошибок устраняются и как именно это происходит. _>>Вооот! Это уже деловой разговор. И у меня тоже есть чёткое понимание этого вопроса в Rust'а — я вполне согласен, что есть целые классы ошибок, от которых он защищает. Но вот в области многопоточности Rust точно не способен дать гарантии корректности. WH>Как мы выяснили, раст ты не знаешь. А значит делать выводы о том, что он может, не можешь.
Я тут пока ТАКОЕ увидел в твоём последнем сообщение (описание будет в следующем моём сообщение), что вообще в шоке. Как вдруг выяснилось один из авторов Nemerle (который позиционировался как улучшенный C#) даже не представляет как работает одна из базовых возможностей C#! Что уж тут говорить о твоём новом увлечение... ))) Ну не буду забегать вперёд — то твоё сообщение настолько фееричное по всем пунктам, что его точно надо будет посмаковать.
_>>Что-то я не понял с чем ты тут споришь собственно говоря. Может просто из принципа, что нельзя соглашаться, а надо обязательно хоть в чём-то возразить? ))) В данном примере мьютекс используется грубо говоря для отсылки сообщения из одного потока в другой (это собственно говоря основной предназначение таких переменных, причём чаще всего уведомляют не один поток, а несколько), а не для разделения каких-то рабочих данных между несколькими потоками. WH>Ох. Conditional variable используют для того чтобы сказать что данные которые защищает мьютекс изменились.
Это исключительно в твоих фантазиях. В реальности и поводов для уведомления других потоков может быть множество и мьютекс там сидит для совсем других целей (можешь попробовать понять здесь https://habr.com/ru/post/278413/).
_>>Нет, они работают потому, что часто мьютекс может быть просто свободен (ждать не надо вообще) и соответственно если сразу уходить в ядро, то переключение контекста будет сделано впустую. Если же вызывать этот код только при занятом мьютексе, то он вполне оправдан, т.к. переключать контекст всё равно надо. WH>Что такое spinlock и почему их используют не знаешь.
Ага, расскажи это тому, кто программирует микроконтроллеры. )))
_>>Требования на упорядоченность сообщений в модель акторов не входит, так что никто на неё и не рассчитывает. WH>Но это гонки по определению.
Только если не подозревать о таком и специально строить код с расчётом на упорядоченную очередь сообщений. )
_>>И каким образом компилятор поймёт какие участки кода возможны для "параллельного" исполнения? ) В реальности это может быть произвольный код грубо говоря между двумя вызовами "await" (который кстати может быть ещё и не только явный, но и внутри других функций). WH>Иди читай про Sync, Send и borrow checker. Ибо если бы ты знал, как это работает, то не задавал бы глупых вопросов.
Ну т.е. ответить на вполне конкретный вопрос тебе нечего? Хотя он вроде бы такой простой и глупый по твоим словам...
_>>Так если код дальше будет банально требовать на входе переменную какого-то фиксированного типа, а ты подсунешь другой тип (пускай и с аналогичными методами), то ты думаешь этот код заработает? Это возможно только если весь дальнейший код написан в стиле шаблонов, чего тебе никто не обещал (и вообще редко бывает в небиблиотечном коде). WH>Мы вроде говорим про свой код, который контролируем. А значит, можем спокойно изменить сигнатуру. WH>Ну и главное ты так и не сказал, зачем нужен этот говнокод.
В статических языках он не то чтобы не нужен, а просто невозможен. Собственно исключительно из-за твоего утверждения что "динамика тут ни при чём, можно легко сделать и на статике", мы и обсуждаем этот вопрос. А для чего это может понадобиться в динамических языка я тебе ответил буквально в одном сообщение выше по ветке — у тебя реально что-то странное с памятью.
_>>Эээ что? Какой ещё тип для разделения между потоками? Ты похоже уже привык к Rust'у и не можешь мыслить по другому (хотя как-то быстро это у тебя произошло, после Nemerle). В нормальном языке нам не надо плодить специальный тип для разделения данных — мы просто работаем с существующими переменными самых обычных рядовых типов. WH>Я привык не писать говнокод. WH>И когда хоть немного проектируешь систему, такое получается на любом языке.
Ну так опиши тогда в чём глубокий архитектурный смысл заведения специального типа, агрегирующего (если это вообще возможно) набор данных, разделяемых потоками. Ну помимо того, что в Rust'е без этого будут большие проблемы... )))
WH>>>Раз это явление повседневное то ты должен легко дать ответ на этот вопрос. _>>На какой вопрос то? WH>Когда нужны данные с разным временем жизни под одним мьтексом?
Когда нескольким потокам требуется параллельная работа с набором переменных, у которых разное время жизни (например одна глобальная переменная и одна выделенная в куче в процессе инициализации приложения). Странно, что тебе это непонятно.
WH>Ты говоришь это явление повседневное. WH>Но ни ты. Ни другие люди так и не показали жизненный пример, когда такое нужно.
И как должен выглядеть" жизненный пример", чтобы он тебя удовлетворил? ))) Простейший код с демонстрацией идеи тебе почему-то не подошёл. Так что надо то? )
_>>Ха, BC в данной реализации мьютексов служит исключительно для предотвращения сохранения ссылок (хотя указатель сохранить никто не мешает, как мы видели) на данные под мьютексом. А для собственно реализации подобного мьютекса достаточно языка с наличием дженериков и RAII. Таких языков в данный момент не мало, но что-то не припомню именно такой реализации мьютекса где-то ещё. WH>Без BC от такой реализации нет никакого толку.
Ещё раз: BC всего лишь не даёт сохранить ссылку на данные, хотя позволяет сохранить указатель. Ты сейчас скажешь что указатель — это unsafe и не надо так делать. Но ведь я же точно так же могут сказать что "сохранять ссылку или указатель на данные в мьютексе — это unsafe и не надо так делать" в любом другом языке с RAII. Так в чём же тогда концептуальная разница?
Здравствуйте, ·, Вы писали:
_>>>>Что за unsafe в Java? _>>·>sun.misc.Unsafe, jdk.internal.misc.Unsafe. _>>Ну если оно есть в Java, то ты конечно же сможешь показать мне описание этого API в официальной документации Java, да? ))) ·>Причём тут официальная документация?
Потому что если какой-то API предлагается к использованию, то он обязательно подробно описывается авторами в официальной документации. Не так ли? )
_>>И какое это имеет отношение к программированию на языке Java? ))) А то такими темпами можно начать утверждать, что аналитику на Питоне надо обязательно знать тонкости управления памятью процессором, ведь из Питона же можно вызвать код на Ассемблере! ·>Так это следствия из твоих утверждений, вот и бред.
Вообще то я как раз утверждал полностью обратное. Я говорил что разработчику на Java (так же как и на Питоне и на других языках со сборщиком мусора) не надо знать низкоуровневых приёмов управления памятью. А ты как раз начал это опровергать, обосновывая бредом про FFI.
_>>>>Ссылки C++ никак не относятся к механизму управления памятью — они её не выделяют и не освобождают. _>>·>Зато они позволяют на ровном месте обратиться к освобождённой памяти. _>>Только при их неверном использование. ·>Сформулируй "неверное использование".
Ссылки в C++ — это идеальный инструмент для передачи данных при вызове функций (т.е. вниз по стеку). В этой роли они эффективны и безопасны. Применение их для других целей практически всегда (на самом деле я не припомню вообще ни одного контрпримера, но на всякий случай оговариваюсь — мало ли какие сценарии я сходу не могу вспомнить) является не верным.
_>>И я естественно говорю не о внимательности программиста, проверяющего руками каждое использование, а об общем концептуальном подходе (который кстати можно и как правило статического анализатора задать) ·>Нельзя задать.
Я смотрю ты большой знаток статических анализаторов. )))
_>>по применению данного инструмента. ·>Это опять какой-то бред. Концептуальный подход — это всё бла-бла. Вот тебе моя гениальная концепция: "Пишите программы без багов". Вот я изобрёл абсолютно безопасный язык программирования. Осталось сделать официальную документацию и готово, всем твоим критериям гарантий безопасности удовлетворяет. Нобелевку мне, можно две.
Непонятно к чему весь этот глупый сарказм, если вся индустрия сейчас именно так и работает.
_>>Например в современном C++ коде должно быть практические исключено (за редкими исключениями, типа использования unsafe в Rust) использование явных new и delete. ·>И что? Это избавит, скажем, от утечек, вызваных циклическими зависимостями? Или тупо от обращений к невалидной памяти?
Естественно. Ну если конечно не рассматривать взаимодействие с какими-нибудь библиотеками на C и т.п., что не поддерживает безопасные инструменты C++.
_>>·>Ну и не говори об автоматизме. Кто ж тебя заставляет? _>>Вообще то в моей аналогии речь как раз про автоматизм и была. ))) Как в управление памятью, так и во многопоточности. И ты попытался утверждать, что Rust умеет автоматическое управление в области многопоточности. А когда я тебе показал что не имеет, ты говоришь "Ну и не говори об автоматизме. Кто ж тебя заставляет?"? ·>Нет, я не пытался утверждать такое. Или цитату в студию.
В ответ на мою цитату "В управление многопоточностью подобного решения пока к сожалению не существует!" (под подобным там выше явно указывалось автоматическое) ты написал длинный хвалебный текст про мьютексы в Rust'е — как это ещё можно было воспринимать? Т.е. если ты не спорил этим моим утверждением, то к чему был весь этот текст, просто ради того что бы хоть куда-то его написать? )))
_>>Так вот, даже реализация Atomic из стандартной библиотеки даёт доступ не только к простейшим гарантированным операциям типа fetch_add, но и к базовой функциональности CAS, которая при неаккуратном использование способна привести к потере данных. Ну а если этого недостаточно, то мы легко можно реализовать свой (более продвинутый чем-то) Atomic, который компилятор будет спокойно воспринимать и в котором при этом будут ещё более уязвимые места. ·>Кул стори, бро. Тебе осталось понять и рассказать какое это всё отношение имеет к data race.
Ну очевидно же. Язык позволяет сказать компилятору: просто поверь мне, что этот тип данных безопасен для одновременного обращения из разных потоков. Так что даже если мы там нарисуем ничем не синхронизированный доступ к общей памяти, компилятор спокойно это проглотит. Понятно, что никто специально такое делать не будет, но вот допустить ошибки при реализации какого-нибудь хитрого контейнера можно без проблем...
_>>На самом деле ничего страшного, конечно же при условии что ты не упёртый фанатик, верящий в миф типа "если компилятор Rust одобрит код без unsafe, то значит в нём точно всё в порядке с многопоточностю". ·>Пока этот бред я слышал только от тебя. Ты упёртый фанатик?
Ну если по итогу дискуссии в данной темке не останется людей, считающих что Rust решает проблемы многопоточности, то я буду считать свою миссию успешной. )
_>>·>Каким образом эти гарантии будут обеспечиваться? _>>В большинстве языков административно. В редких случаях (типа Эрланга) это может быть упаковано на уровне рантайма языка, но у этого всегда есть соответствующая цена. ·>Верно. В большинстве языков — административно, а в rust — они обеспечиваются компилятором. В этом и отличие.
Нет, в Rust гарантии корректной многопоточности ни не обеспечиваются, о чём честно написано в их документации.
_>>·>Ты таки задачу так и не обозначил. А переводить некий говнокод с одного яп на другой — бессмысленное занятие. _>>В общем случае — возможно. Но в данном случае это языки одного класса, для одной ниши и второй язык рассматривают как потенциальную замену первому. Поэтому в данном случае вопрос "как мне переписать данный работающий код с первого языка на второй" является вполне корректным. ·>Вот тебе: ·>
Кстати, на самом деле тот код на C++ вполне можно повторить в Rust'е практически дословно. Правда оно будет совсем не "канонично", так что у любителей языка видимо рука не поднимается сделать такое. )))
_>>>>Ну ты же понимаешь, что это означает как минимум "Rust не гарантирует отсутствие утечек памяти и других ресурсов, управляемых через RAII"? _>>·>Ну да. И? _>>Просто получается что в данном моменте "как бы гарантированный" Rust на практике имеет меньше гарантий, чем большинство других языков с RAII (включая тот же самый C++). ·>Каким образом C++ обеспечивает гарантии отсутствия утечек памяти?
C++ обеспечивает гарантии работы RAII (гарантии вызова деструктора). Следствием этого может быть и гарантии отсутствия утечек памяти, если применять только современные подходы к её управлению.
_>>Я вроде как ясно написал всё. Ну повторюсь ещё раз, вдруг поможет... Идеально безопасной автоматической многопоточности (так же как управления памятью и всего остального) естественно не существует, просто в силу потенциального наличия бизнес-ошибок в коде. Но если мы предположим, что имеем гарантированно безопасный с точки зрения бизнес-логики код (скажем на неком псевдоязыке или вообще диаграммами), то хотелось бы чтобы при его переводе в код уже на нашем реальном рабочем языке, не возникло никаких дополнительных ошибок. В том смысле что они не возникли бы не из-за внимательности программиста, а из-за принципиальной невозможности их возникновения. ·>Это всё идеал. А конкретика? Что конкретно дают акторы? "точно всё в порядке с многопоточностю"?
Для конкретики задавай конкретный вопросы с конкретными примерами. Писать обзорную статью о преимуществах модели акторов я тут не планировал. )))
Здравствуйте, alex_public, Вы писали:
_>>>Ещё раз: отсутствие проблем гарантируется при соблюдение некоторых условий. И смысл этой "математики" тут в том, что мы заменяем сложные и неочевидные условия, на достаточно простые. Правда, при этом слегка сужая область применения. WH>>Так и в случае с мьютексами правило очень простое. WH>>Нужно захватывать мьютексы в одном порядке. А учитывая, что в 99% случаев код больше одного мьютекса не захватывает то и проблем не возникает. _>Вот что мне нравится в дискуссиях с фанатиками, так это их непробиваемые двойные стандарты. )))
Да уж. Твои двойные стандарты абсолютно не пробиваемы.
Читай выделенное...
Я просто твою аргументацию против тебя развернул.
_>Не совсем, хотя семантика перемещения крайне важна — она была завершающим штрихом к базису в языке, позволяющему реализовать подобные вещи (и многие другие, требующие работы с некопируемыми объектами). А речь была о введение в стандарт языка таких вещей как unique_ptr, shared_ptr/weak_ptr, и сопутствующая им инфраструктура.
Всё ещё хуже, чем я думал.
Это всё было в бусте уже очень давно. Перемещение этого в стандартную библиотеку конечно шаг правильный. Но на революцию и близко не тянет.
Да и руками оно писалось очень просто.
Ну и ещё раз демонстрируешь своё полное непонимание, что такое BC.
Ибо к BC это всё не имеет отношения.
_>>>Например сопрограммы (на уровне языка, со всеми вытекающими из этого вкусняшками), WH>>В rust это тоже скоро будет. _>Странно что их не было изначально,
По тому, что безопасная zero-overhead реализация оказалась нетривиальной задачей. Но её решили.
_>всё же Rust вроде как современный язык, а уже отстаёт от старичка C++. Кстати, а какие они будут, stackful или stackless? У каждого из этих типов есть свои преимущества.
stackless
_>В C++ уже много лет как доступны stackful сопрограммы через библиотеку Boost.Coroutine2. А сейчас stackless реализацию приняли в стандарт языка.
Такое и для rust'а есть.
_>Ты серьёзно не понимаешь? ))) В C++ ведь постоянно идёт inline кода, и если ты например используешь чужую функцию в которой есть ветвление в зависимости от значения параметра, а в твоей функции стоит контракт на значение этого параметра, определяющий "if", то в итоговом коде уже не будет ветвления. То же касается просто всяческих проверок, выборов типа алгоритма и т.п. Можешь ознакомиться здесь http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/p0147r0.html подробнее.
И чем это лучше для оптимизатора чем:
if (...)
throw ...;
всё то же самое.
_>Не не не, ты не путай. Если бы речь шла о метапрограммирование (в C++ оно на шаблонах, как ты знаешь), то я бы тут тоже много новинок в C++ мог вспомнить (типа введения концептов, строк в шаблонах и т.п.), но я в курсе что в Rust'е с этим всё хорошо и поэтому даже не заговаривал на эту тему. А здесь была речь совсем о другом: о constexpr. Это не метакод, а самый обычный код, состоящий из обычных функций, которые можно точно так же вызвать и из рантайма. Но при этом все вычисления производит компилятор в процессе сборки.
Так макросы этим и занимаются.
_>>>И думаю что если бы этот кусочек переписали бы на современный C++, то результат был бы в точности такой же. WH>>За счёт чего? BC нет. Реализуемых компилятором трейтов Sync и Send нет. _>И это давно уже обсуждалось. Посмотри например это http://www.rsdn.org/forum/philosophy/6963027?tree=tree сообщение и остальные по ветке ниже.
Понятно. За счёт чего сказать не можешь.
_>>>Тебе уже наглядно продемонстрировали, что не будет. WH>>Мы говорим про Option<Box<Data>> на который ты возбудился. WH>>Так что ещё как будет. _>Это в твоих фантазиях мы говорим про это, причём я тебе уже раза два на это указал.
Так ты же возбудился на этот код.
_>Я же говорю о необходимости использовать контейнеры в Rust в тех случаях, когда в C++ достаточно обойтись обычной переменной. Более того, я тебе продемонстрировал конкретный пример кода, на котором ты слился.
Ты продолжаешь сливаться.
Зачем в реальной жизни нужен твой код?
Ты не можешь ответить на этот вопрос.
WH>>>>Так ты покажи. Я когда на rust код писал, что-то ничего страшного не заметил. _>>>Ну сделай поиск, сколько unwrap'ов в твоём проекте? WH>>Ни одного. _>
Тем не менее, это правда. Если ты грамотно пишешь код, то unwrap не нужен.
_>>>И если что, все эти вызовы являются чистым синтаксическим мусором, существующим исключительно для правильной работы BC. _>Я курсе про обработку ошибок. Более того, года 3 назад на этом форуме была большая дискуссия на эту тему, в которой я участвовал и в которой обсуждался в том числе и Rust. Но нюанс в том, что без лишнего контейнера, из которого надо попытаться вытащить данные, этого кода просто не было бы.
Нюанс в том, что к BC это отношения не имеет от слова совсем.
А ты что понаписал? Я специально твои слова оставил.
_>Кстати, в том же C++ механика сходная (с использованием unwrap), хотя поменьше синтаксического шума — традиционно тут просто используется переопределённый оператор *, который в случае ошибки кидает исключение. Однако в моём изначальном сообщение речь шла не об этих мелочах, а именно о лишних контейнерах.
В С++ для обработки ошибок используют исключения. Поэтому unwrap'а там вообще нет.
WH>>1)В 99% случаях работать из множества потоков не нужно. WH>>2)Не путай мьютексы в rust с мьютексами во всех остальных языках. В rust с мьютексом накосячить очень сложно. _>Мы же уже обсудили, что мьютексы Rust'а никак не защищают от дедлоков и прочих ошибок многопоточности. Ты снова начинаешь? )))
1)Более 99% проблем с мьютексами это работа с данными без захвата мьютекса.
Раст это исключает.
2)Дедлоки и на акторах поймать можно.
_>>>Ну и да, с моделью акторов эта хрень в любом случае не дружит, по целому ряду причин. WH>>Назови хоть одну причину, почему она не будет работать с акторами в rust'е? BC передаёт привет. _>Модель актров НАПРЯМУЮ ЗАПРЕЩАЕТ РАЗДЕЛЯЕМЫЕ МУТАБЕЛЬНЫЕ ДАННЫЕ. Так понятно? )))
Нет. Не понятно.
BC не даст сохранить ссылку на разделяемые данные.
А read handle по сути ни чем не отличается от ссылки на актора.
С точки зрения модели акторов транзакция чтения из evmap посылка сообщения другому актору и получение ответа.
_>Я уже молчу про потребность использования мьютексов, которая точно так же напрямую противоречит модели мьютексов. Но на фоне первого косяка это уже просто ерунда.
Эту потребность придумал ты. Более одного писателя обычно не нужно.
Так что держим write handle внутри одного актора, где и производим все изменения.
А остальные акторы будут просто подглядывать в память к этому актору.
При этом с точки зрения модели акторов это будет обмен сообщениями.
Только очень быстрый.
WH>>А в случае с rust за этими правилами следит компилятор. И ты не сможешь их нарушить. _>Вау, Rust имеет какие-то удобства по сравнению с C+WinAPI, да не может быть. )))
Раст даёт гарантии. А C+WinAPI нет.
Но ты пишешь про то что C+WinAPI даёт гарантии.
_>Ну т.е. обсуждать что надо поправить в коде (например взять вместо локальной переменной, выделенную в куче) ты не хочешь, а предпочитаешь вместо конкретных технических вопросов продолжать отмазываться с помощью демагогии. Понятно всё с тобой.
Нет, это ты не можешь сформулировать задачу.
Ты не можешь привести реальный пример из свой жизни. Не смотря на то, что говоришь, что такое случается постоянно.
У меня для тебя два варианта:
1)Ты никогда такое не писал и намеренно лжёшь. Зачем?
2)Ты понимаешь, что написал говнокод и боишься позора. Но зачем ты тогда поднял эту тему?
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, alex_public, Вы писали:
WH>>Именно на уровне языка это определение и существует. _>Да? А почему тогда компилятор не отследил его нарушение и не ругнулся?
По тому, что ты написал unsafe. Твой КО.
WH>>И если тебе нужно написать unsafe, то ты должен соблюсти все обещания что ты сделал в сигнатуре функции. _>Ну и? Какие обещания сделаны в сигнатуре этой функции
Возвращаемая ссылка будет иметь время жизни 'static
_>и почему они не соблюдены?
По тому, что ты написал unsafe и отключил проверку кода компилятором.
Но когда ты пишешь unsafe, ты берёшь проверки на себя. Но ты это не сделал.
А вот автор evmap это сделал. Да у evmap внутри есть unsafe. Но при этом все обещания, которые делает публичный контракт evmap, выполняются методами, которые компилятор отследить не может.
Собственно для этого unsafe и нужен.
_>Т.е. ты предлагаешь пользователям зависших приложений устанавливать какие-то системные утилиты, делать дамп всего адресного пространства процесса и отсылать его производителю ПО? Я правильно тебя понял?
Пользователи бывают разные.
1)Если это серверное ПО то админы настраивают автоматическую отсылку дампов.
2)Некоторое массовое ПО таскает с собой такие утилиты и если оно упало формирует дамп и предлагает пользователю одним кликом отправить его разработчику.
WH>>Как мы выяснили, раст ты не знаешь. А значит делать выводы о том, что он может, не можешь. _>Я тут пока ТАКОЕ увидел в твоём последнем сообщение (описание будет в следующем моём сообщение), что вообще в шоке.
Это я в шоке от того что ты там понаписал.
Но что характерно ты опять увёл разговор в сторону.
Так как насчёт того чтобы признать что unwrap к BC отношения не имеет?
WH>>Ох. Conditional variable используют для того чтобы сказать что данные которые защищает мьютекс изменились. _>Это исключительно в твоих фантазиях. В реальности и поводов для уведомления других потоков может быть множество и мьютекс там сидит для совсем других целей (можешь попробовать понять здесь https://habr.com/ru/post/278413/).
Что за хрень ты опять несёшь?
Там весь пример про защиту внутренних структур аллокатора от гонок. И CondVar используется для оповещения о том, что появилась свободная память.
Этот пример на 100% в мою пользу.
_>>>Требования на упорядоченность сообщений в модель акторов не входит, так что никто на неё и не рассчитывает. WH>>Но это гонки по определению. _>Только если не подозревать о таком и специально строить код с расчётом на упорядоченную очередь сообщений. )
Тем не менее, это гонки.
WH>>Иди читай про Sync, Send и borrow checker. Ибо если бы ты знал, как это работает, то не задавал бы глупых вопросов. _>Ну т.е. ответить на вполне конкретный вопрос тебе нечего? Хотя он вроде бы такой простой и глупый по твоим словам...
Ну, ты же на мои вопросы не отвечаешь. Даже не говоришь, что нужно прочитать.
А пересказывать туториалы раста мне, честно говоря, лень.
_>В статических языках он не то чтобы не нужен, а просто невозможен. Собственно исключительно из-за твоего утверждения что "динамика тут ни при чём, можно легко сделать и на статике", мы и обсуждаем этот вопрос. А для чего это может понадобиться в динамических языка я тебе ответил буквально в одном сообщение выше по ветке — у тебя реально что-то странное с памятью.
Ты не ответил.
_>Ну так опиши тогда в чём глубокий архитектурный смысл заведения специального типа, агрегирующего (если это вообще возможно) набор данных, разделяемых потоками. Ну помимо того, что в Rust'е без этого будут большие проблемы... )))
В том, что мы точно знаем, что мы между потоками разделяем. Твой КО.
Мне просто дико, что такие банальности нужно пояснять.
_>Когда нескольким потокам требуется параллельная работа с набором переменных, у которых разное время жизни (например одна глобальная переменная и одна выделенная в куче в процессе инициализации приложения). Странно, что тебе это непонятно.
Тут нет задачи. Тут есть решение.
_>И как должен выглядеть" жизненный пример", чтобы он тебя удовлетворил? ))) Простейший код с демонстрацией идеи тебе почему-то не подошёл. Так что надо то? )
Задачу.
Что-то типа: Вот я писал программу, которая должна решать определённую задачу. И для решения этой задачи мне был нужен такой код.
_>Ещё раз: BC всего лишь не даёт сохранить ссылку на данные, хотя позволяет сохранить указатель. Ты сейчас скажешь что указатель — это unsafe и не надо так делать. Но ведь я же точно так же могут сказать что "сохранять ссылку или указатель на данные в мьютексе — это unsafe и не надо так делать" в любом другом языке с RAII. Так в чём же тогда концептуальная разница?
В том, что в другом языке контроля вообще нет. Там весь код всегда unsafe. А в расте контроль нужно явно отключать.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, ·, Вы писали:
_>> global_var=await process_asyc(global_var, param);//process_asyc — длительная асинхронная операция _>>Так вот, надеюсь не надо пояснять, почему результат работы этого теперь уже некорректного кода будет зависеть от случайных факторов? При этом вся работа с проблемной переменной будет происходить исключительно в рамках одного потока... ·>Ок, хороший пример, давай я помогу тебе разобраться. В случае однопоточного асинхронного исполнения будет race condition, и в итоге результат выполнения будет недетерминирован, но не являться undefined behaviour. Недерминированность выражается в том, что значение global_var будет одним из значений process, но неясно каким из. ·>Если это же сделать многопоточным, т.е. эту global_var будут пытаться писать одновременно разные потоки без всякой синхронизации, то возможен data race и значение global_var может быть каким угодно. ·>Принципиальная разница в том, что в случае недетерминизма мы можем анализировать код и делать какие-то заключения (reasoning) о поведении данной части кода и программы в целом, то в случае наличия UB — мы можем сказать лишь одно: behavior of the program is undefined. ·>Например, допустим мы знаем, что process возвращает только положительные числа. ·>В случае недетерминизма, мы можем смело утверждать, что global_var будет положительным числом. ·>В случае же undefined behaviour — в переменной может оказаться всё что угодно, никаких гарантий нет. Притом этот результат может зависеть от версии компилятора, от железа, от операционки, от погоды на марсе и т.п. ·>Кстати, если присвоение global_var будет атомарным, то опять получится недетерминизм, а не undefined behaviour.
Значит по тезисам:
1. В данном конкретном примере присвоение global_var как раз будет атомарным. Потому как это int и C++ следит за выравниванием. Как это будет происходить на низком уровне скажем в процессорах Intel можешь глянуть например здесь https://software.intel.com/en-us/articles/software-techniques-for-shared-cache-multi-core-systems в пункте Avoid false sharing.
2. Так что поведение в асинхронном и многопоточном варианте будет абсолютно идентично.
3. Undefined behaviour очевидно не является определением data race, а является всего лишь следствием.
4. Насколько я вижу, вся эта дискуссия с тобой свелась к спору о терминологии, что весьма глупо. Лично мне абсолютно всё равно как называть конкретные явления, лишь бы договорить об одном языке для всех. Я могут сформулировать свою мысль в любой терминологии — мой основной тезис вообще не зависит от данного выбора.
5. Ну и наконец тот самый главный тезис: как ни называй описанную проблему, Rust никак от неё не защищает.
_>>·>В случае конкурентного чтения и записи, например, атомарость говорит о том, что читатель увидит либо старое значение, до записи, либо новое, после записи и ничто другое. Да, атомарность нужна для интов. _>>Правильно. Только вот ассемблерная инструкция перемещения регистра в память (что равносильно оператору равенства в твоём языке программирования) является вполне себе атомарной, ·>Для какого CPU? А про membar-ы не забыл?
Ну если ты знаешь CPU, в котором перемещение регистра в ячейку памяти является не атомарным, то с удовольствием послушаю про такой. )))
А причём ты тут приплёл барьеры вообще не понятно — они работают только с переупорядочиванием инструкций, никак не влияя на их свойства.
_>>так что для int'ов(подразумевая под ним машинное слово) оно и так автоматически имеется. ·>Если бы С++/Rust/etc стандарт писался ровно для одного CPU и т.п., то возможно твоё утверждение было бы и верным.
Ну приведи хоть один контрпример. ))) Любой CPU, в котором это не так.
_>>Поэтому для ограниченного числа задач на самом деле можно параллельно обращаться к одному int'у и всё будет нормально. ·>"А у меня всё работаииит!"
На самом деле на многих ещё актуальных аппаратных платформах просто нет специальных атомарных инструкций, как ты думаешь, как там выживают? )))
_>>Проблема возникает если надо использовать корректное предыдущее значение — тогда на помощь приходит CAS. ·>А так же проблема возникает ещё в 100500 случаев, например, если этот конкретный int вдруг невыровненный оказался.
Ну скажем в C++ он просто так невыровненным не окажется — у нас полный контроль за этим. )))
_>>Кстати, по этой ссылке дальше идёт ещё более интересный текст про многопоточность в Rust в общем. Что никаких гарантий естественно нет и единственно чем реально озабочем тут Rust, это сохранение memory safety — как я собственно и говорил изначально. ·>Там про race condition.
И? О того что ты назовёшь это по другому, у тебя некорректный код вдруг заработает правильно? )))