Здравствуйте, WolfHound, Вы писали:
_>>Если ты реально считаешь, что Rust даёт гарантии отсутствия гонок, то у меня для тебя неприятные новости... Причём насчёт твоей квалификации в данной теме в целом.
WH>Код в студию. Покажи, как без unsafe получить доступ к памяти без надлежащей синхронизации.
А для того, чтобы получить проблемы с многопоточностью совсем не обязательно получать доступ к памяти мимо мьютекса. Более того, как раз мьютексы и вызывают довольно большую часть проблем (например
https://users.rust-lang.org/t/using-rayon-in-code-locked-by-a-mutex/20119 — ты помнится прямо эту библиотечку тут хвалил?) в многопоточном коде. Так что твои смешные идеи в стиле "просто поменяем последовательный запуск кода на параллельный и компилятор отследит все некорректности" — это очевидно фантастика.
_>>Ну и гарантии естественно есть. Разве что они рантаймовые — будет сообщение об ошибке, в случае попытки доступа не под мьютексом.
WH>Вот тут давай объясняй, как питон это делает.
WH>Звучит, как самолёты подлетают к границе и падают.
Не "Питон это делает", а такое можно сделать на Питоне. И я вполне подробно описывал как в сообщениях выше — просто модифицируем код (грубо говоря вешаем декораторы на всех его членов, плюс добавляем пару новых методов типа lock/unlock) того объекта , доступ к которому надо защитить мьютексом. Естественно это не программист руками будет делать, а всё произойдёт автоматически при подключение защиты к данному объекту — у динамических языков есть свои преимущества.
_>>Какая ещё часть объекта? Понятно же, что под набором данных подразумевался не кусок какого-то объекта, а наоборот набор разных — на практике только так и встречается собственно. И для реализации автоматического доступа через мьютекс к этим данным, тебе придётся добавить лишний тип, агрегирующий в себя все эти объекты (а так же переписать везде код, чтобы доступ был только через экземпляр этого нового типа). Более того, с учётом специфики Rust'а, у тебя ещё возникнут сложности в случае неравного времени жизни (а это тоже очень даже вероятный сценарий) этих объектов.
WH>Давай про свою практику рассказывай подробнее. Ибо в моей практике такое не встречалось вообще ни разу. Всегда разделяемые данные естественным образом оказывались в одном объекте.
Звучит как фантастика.
WH>Особенно интересует пример, в котором объекты под мьютексом живут разное время.
Ну вот тебе компилируемый пример на C++ с защитой набора разделяемых данных мьютексом:
int global_shared=1;
int main()
{
int local_shared=1;
vector<thread> pool;
mutex m;
for(int i=0; i<30; i++) pool.emplace_back([&]{
lock_guard<mutex> lock(m);
auto sum=global_shared+local_shared;
global_shared=local_shared;
local_shared=sum;
cout<<global_shared<<' ';
});
for(auto& t: pool) t.join();
}
Можно увидеть его точный аналог на Rust, с размещением данных внутри мьютекса? )
_>>Ну открой код Rust'а и посмотри — просто контейнер, агрегирующий некий объект и возвращающий ссылку на него по функции Lock, в случае языков без RAII. А в случае наличия RAII в языке, будем возвращать специальный объект для автоматического отпускания мьютекса, точно так же, как и в Rust.
WH>Да причем тут вообще RAII?
WH>Я не понимаю, как ты работу с мьютексами организуешь. RAII это мелкие детали реализации.
WH>Сначала я подумал, что ты предлагаешь засунуть мьютекс в объект и в каждом методе его захватывать. Так очень часто делают.
WH>Можешь без юления объяснить, что ты делаешь?
Мьютекс в защищаемый объект — это в случае динамических языков. В статических мьютекс хранится в объекте-контейнере, рядом с защищаемыми данными. Ну и захватывать в каждом методе несколько неэффективно — для захвата не проблема предусмотреть два метода (в случае наличия RAII достаточно одного) типа Lock/Unlock. А в вызове методов достаточно проверять факт захвата (для динамических языков).
Ну и кстати говоря у Rust'а точно такая же реализация, только там контейнер и мьютекс изначально объединены в одну сущность.