O>>Хотя, с костылями супротив meltdown я в этом уже не уверен. ЕМ>А Вам попадались работающие реализации Meltdown, которые реально что-то добывают из ядра или памяти смежных процессов?
даже не пробовал
Как много веселых ребят, и все делают велосипед...
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>А Вам попадались работающие реализации Meltdown, которые реально что-то добывают из ядра или памяти смежных процессов?
Мне — нет. Хотя пробовал. Но, афаик, опубликована информация была в след за патчами, а ковыряться в старых версиях ОС мне лень. Да и вообще, меня, к примеру, дома, этот вопрос не колышит вообще. А сервер с виртуалками, вполне волнует.
Здравствуйте, Serginio1, Вы писали:
S> Операция записи как раз не потокобезопасна. Пример из БД. Тебе нужно модифицировать набор записей в котором присутствует касаа. S>Ту не можешь одновременно списать деньги, так как их может оказаться меньше. Но транзакция может и не завершиться если по каким то причинам эти деньги спасить нельзя. S>И вторая транзакция должна дождаться, пока первая не закомитится или не откатится.
Ок, понял, только один вопрос, зачем здесь многопоточность? Если запись не идет параллельно, то зачем нужно много записывающих потоков, они будут толкаться на записи, и мешать друг другу. Запись транзакций в базу, как-раз отличный пример, где могопоточность не нужна, а неблокирующий ввод/вывод отлично справится (запись в базу не блокируется).
S> Если ты используешь общие данные только на чтение, то и блокировки не нужныю
ну вот почему бы их не скопировать во все процессы (будет работать быстрее), а если их много для копирования во все процессы, то можно разместить в shared memory? Тут потоки может и дают некоторое удобство, но ты всегда рискуешь, что кто-нибудь потом, своими шаловливыми рученками решит добавить апдейт данных. Лично я даже в случае потоков, постарался бы скопировать данные, чтоб они были локально у каждого потока.
S>>>Там где есть запись обычно работают блокировки ReadWriteLock K>>Ужасно тормозная вещь, имеет смысл, толко когда у очень тебя много параллельных чтений и очень мало записей. S> Так так в большинстве и существует. Берем ту же БД и транзакции. Там в большинстве блокировки именно на чтение
Ок, впринципе, допустим, тут действительно нужен такой паралеллизм. Много чтений, и редкие записи. Опять же, что мешает расшарить коннекшен базы данных между процессами, и так же синхронизировать его?
Возможно в .Net нельзя использовать rw lock через shared memory? В линуксе это точно можно сделать.
S> Прежде чем говорить, ты бы пример привел где эти тормоза. Вот взаимодействие между процессами там дикие тормоза получишь.
Большинство потокобезопасных контейнеров содержат внутри себя блокировку, то есть чтение и записть в них идет последовательно. Есть правда еще lock free контейнеры, но судя по всем тестам, такие контейнеры выигрывают у обычных поткобезопасных в очень редких случаях. Моя практика показывает, что какой-нибудь хеш-мап, который расшаривается между потоками часто является узким местом, и в итоге производительность от многопоточности не увеличивается, а скорее проседает. Но тут надо конечно смотреть по задаче, вообще главное в многопоточных программах — это стараться как как можно меньше расшаривать данные между потоками. Поэтому таких контейнеров надо стараться избегать. Но я признаю, что иногда это полезно. Но теоретически контейнер можно расшарить между процессами, поэтому многопоточность тут нужна скорее от убогости API процессов, с которыми это сделать сложно, чем именно от необходимости иметь именно потоки с полностью общей незазизенной памятью.
S> Не синхронизация, а блокировки. Синхронизация это выполнение кода в одном потоке.
Нет, термины "синхронизация потоков", "примитивы синхронизации" вполне устоявшиеся для обозначения того, что ты называешь "блокировками".
S>Но если ты используешь асинхронность тот же JavaScript работает на одном потоке, но доступ к данным из разных задач не защищен.
Да, но там не будет race condition и deadlock! Именно это ошибки, которые могут невыявиться тестированием, и именно от них надо защищать данные при паралеллизме. Там конечно тоже все не просто, но, тестирование по крайней мере позволяет эффективно отловить баги. То есть главная проблема многопоточности исчезает!
S>Ничего не надо ставить если используешь только чтение.
Уже ответил на это
S> Угу проходили. У меня на некоторых вещах стоят семафоры, что бы контролировать количество одновременных запросов. Поверь ты не прав. S>Многопоточность дает практический пропорциональный прирост количеству потоков (по количество ядер + учет хайпертридинга и на самом деле больше так как внутри могут выполняться асинхронные запросы к диску, БД на другом
компе итд)
Я имею ввиду, один поток, плюс неблокирующее io, чтоб работали одновременные запросы и к диску и к бд и тд. А дальше, эту ассинхронщину можно разделить на несколько воркеров (процессов, ну или потоков, если уж ты так хочешь). Именно такие решения обладают максимальной производительностью. Но я конечно не знаю твою специфику, поэтому конечно мои советы нельзя воспринимать слишком серьезно. S>Просто они нужны, что бы давать отлуп иначе сервер не справляется аля DoS-атака.
S> Еще раз в большинстве случаев происходит чтение данных , в большинстве случаев к базе данных. Нет проблем с многопоточностью. S>Твои проблемы надуманы.
Ну, у разных людей разные проблемы, если ты единственный разработчик на проекте, ты очень четко знаешь и следишь за тем, что может шариться между потоками а что нет, и все работает стабильно, то впринципе используй... все так делают, ничего ужасного в этом нет. Но, если у тебя вдруг ни с того ни с сего после долгой стабильной работы когда-нибудь выскочит race condition, можешь вспомнить обо мне
Здравствуйте, ononim, Вы писали:
MA>>Прикладные задачи — это же не сумму массива посчитать, а и в ядро сбегать. А значит — переключить контекст. O>В ядро сбегать != переключить контекст. TLB не флашится, например. Хотя, с костылями супротив meltdown я в этом уже не уверен.
Там же еще супротив spectre залипухи теперь вроде стоят. Я правда не помню реализацию. Вроде воткнули пару инструкций что-ли. Хотя, текущее состояние уже и не знаю. Читал, но забыл за ненадобностью.
Здравствуйте, Sharowarsheg, Вы писали:
K>>А чем большие задачи отличаются в данном контексте?
S>Накладные расходы на всякую мелочёвку начинают отнимать неделю времени вычисления.
Ок, я предположу, что ты у тебя действительно есть пример, где замена потоков на процессы может дать существенную просадку в производительности. Но все-таки хотелось бы большей конкретики?
K>>Даже крутые гуру часто не могут написать, что-то более менее сложное без дедлоков и гонок. S>Это только первые десять лет сложно.
Больше десяти лет опыта, а все еще юношеский максимализм... Скажи еще, что у тебя вообще в коде багов не бывает.
K>>Есть одна разница, то, что у тебя есть гонки, ты можешь узнать только через год стабильной работы на продакшене, когда уже разработчик давно уволился. Это главная фишка дедлоков и гонок, что они могут не отлавливаться даже очень тщательным тестированием.
S>Дедлоки и гонки не нужно выявлять тестированием, их нужно не создавать при проектировании.
Круто, скажи еще, что у тебя все точно работает как спроектировал вначале после 10 лет поддержки и доработки.
Вообще завидую людям, которые не работали с крыпными проектами, где много legacy, не сталкивались с индусским кодом. Спроектировали, написали, и все сразу работает правильно и без багов, и дорабатывать ничего не надо, потому что хорошо спроектировано. Хорошо наверное живется в стране эльфов!
Здравствуйте, ksandro, Вы писали:
S>>И вторая транзакция должна дождаться, пока первая не закомитится или не откатится. K>Ок, понял, только один вопрос, зачем здесь многопоточность? Если запись не идет параллельно, то зачем нужно много записывающих потоков, они будут толкаться на записи, и мешать друг другу. Запись транзакций в базу, как-раз отличный пример, где могопоточность не нужна, а неблокирующий ввод/вывод отлично справится (запись в базу не блокируется).
Ну начнем с того, что есть и версионные БД, которые исполняют транзакции параллельно, и синхронизация и разрешение конфликта происходит только во время коммита (ну или раньше, когда возможно). А насчет подсистемы записи — то тут на глазок не скажешь как лучше. С точки зрения пользователя все равно SQL черный ящик, а с точки зрения разработчика, то вероятно асинхронный ввод/вывод вполне годен, а может быть он синхронный по историческим причинам. Код ведь как насечки на каменях, раз высрешь — потом не отмоешься.
S>> Если ты используешь общие данные только на чтение, то и блокировки не нужныю K>ну вот почему бы их не скопировать во все процессы (будет работать быстрее), а если их много для копирования во все процессы, то можно разместить в shared memory? Тут потоки может и дают некоторое удобство, но ты всегда рискуешь, что кто-нибудь потом, своими шаловливыми рученками решит добавить апдейт данных. Лично я даже в случае потоков, постарался бы скопировать данные, чтоб они были локально у каждого потока.
OS никак не гарантирует сохранность "private memory". Все программы надеятся на third-party библиотеки (в линуксе они часть системы, типа уродливого и неповоротливого libc). В клинических случаях — это вообще внедренцы через хуки или видео драйвера. И да — их банят (не дают загружаться, например так делает всеми нелюбимый хром). Кроме того, мягко говоря количество порождаемых процессов в секунду весьма ограничено, по крайней мере на windows. Нужно понимать стоит ли свеч игра в процессы. В добавок процессы наверняка потребуют еще и IPC,что добавляет еще дополнительный уровень сложности.
K>Возможно в .Net нельзя использовать rw lock через shared memory? В линуксе это точно можно сделать.
В дотнете доступны как сырые указатели, так и набор interlocked инструкций. Так что все там можно.
K>Большинство потокобезопасных контейнеров содержат внутри себя блокировку, то есть чтение и записть в них идет последовательно.
И как правило — по сегментам.
K>Есть правда еще lock free контейнеры, но судя по всем тестам, такие контейнеры выигрывают у обычных поткобезопасных в очень редких случаях.
Lock-free хэш-таблицу с открытой адресаций почти никто не делает. Но она чувствительна к кластеризации данных.
K>Но тут надо конечно смотреть по задаче,
Это правильно.
Вообще в целом, я не парюсь об этом. Всегда по умолчанию использую потоко-небезопасные коллекции. Если вдруг начинается многопоточность — то ищу решения. От простого тупого лока вокруг (для многих структур данных он нужен только вокруг записи, делая возможным чтение неблокирующим), так и на днях буквально вставил лок на изменение, но чтение-маппинг значений осуществляется через массив, и лок только при отсутствии маппинга. В задаче это позволило избавиться от долгих лукапов по строкам по словарям (задача не общая, изначально строки ссылаются на таблицу строк). Поэтому морочить голову с rw lock даже нет смысла.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, ksandro, Вы писали:
K>>Как ты говоришь, жизнь боль. Не всегда есть возможность выбрать новый проект с красивым кодом.
НС>И ради кривых проектов ты предлагаешь запретить потоки? Ну ОК, я тебя понял.
Честно говоря, я решил здесь высказать свое мнение о многопоточности в несколько более радикальном виде, чем думаю на самом деле, это я сделал специально, чтоб вызвать дискуссию.
Но меня реально поражают две вещи, первая: почему в мейнстрим языках нет никакой поддержки защиты данных от доступа из разных потоков на уровне компилятора, это же всегда неправильно работает.
И вторая, это то, с какой легкостью народ пишет многопоточные программы, не сильно задумываясь о последствиях (тут тормозит, давай в отдельный поток, тут надо не зависнуть при записи в большой файл, а давай еще один поток).
А потом приходится собирать баги.
НС>>>В твоем языке нет private?
K>>Это что-то новое... Не поделишься как именно private решает проблему доступа из другого потока. Гарантировать можно только потокобезопасность локальной переменной функции. А private никак к многопоточности не относится. Если эту переменную можно как-то менять, то ее можно менять из разных потоков.
Ну, если переменная есть, то откуда-то ее можно менять, и скорее всего public api класса, как-то может сделать так, чтоб эту переменную поменяли. А значит все зависит от того, как вызывать. Можно конечно класс защитить, и добавить lock на все публичные методы, но это как правило приведет к лишним тормозам. Так что privateни на что не влияет.
НС>private гарантирует что менять переменную будет только известный код. А от этого уже проистекает как бороться. Можно в документации написать крупными буквами, что класс с изменяемым состоянием не потокобезопасен, и стараться поменьше таких классов плодить. Для большинства адекватных разработчиков этого вполне достаточно.
Этот класс кто-то заюзал в своем классе, потом тот класс кто-то еще где-то заюзал, потом, кто-то решил вынести код в отдельный поток, и... внезапно мы вдруг получаем race condition, который еще и никто не видет ни на ревью ни при тестировании.
НС>Можно статическую переменную сделать thread static, и тогда эту будут личные половые проблемы того криворукого программиста, что стейт в другом потоке другой.
Наконец, в совсем клинических случаях можно повесить простенький guard, который будет проверять, что изменяющие состояние методы зовутся из того потока, из которого нужно (к примеру, из того из которого позвали ктор), и тогда криворучки будут получать исключение при попытке написать кривой код.
Ну, вот судя по тому, сколько разных техник ты предложил, можно понять, что проблема существует, и что она довольно серьезная.
Здравствуйте, ksandro, Вы писали:
K>Круто, скажи еще, что у тебя все точно работает как спроектировал вначале после 10 лет поддержки и доработки.
У меня один компонент доставшийся от предков, пил кровь. Недо-C с классами. Переписал на дотнете — все прошло. Пока переписывал, нашел баги в спецификациях платежной системы. Устранили.
K>Вообще завидую людям, которые не работали с крыпными проектами, где много legacy, не сталкивались с индусским кодом. Спроектировали, написали, и все сразу работает правильно и без багов, и дорабатывать ничего не надо, потому что хорошо спроектировано. Хорошо наверное живется в стране эльфов!
Завидуй молча.
Если серьезно, то специфики у всех своей столько, что замахаешься разгребать. Не надо таких узких выводов. Я видел код и похуже индусского, код "я менеджер", код "я все сделал", код "там только подправить". Все это по итогу в течении недельного ржача обычно переписывалось на рабочий. Иногда было тяжело переписывать, жалко пёрлы автора не сохранил.
Здравствуйте, ksandro, Вы писали:
K>Но меня реально поражают две вещи, первая: почему в мейнстрим языках нет никакой поддержки защиты данных от доступа из разных потоков на уровне компилятора, это же всегда неправильно работает.
А как, по твоему, это должно выглядеть?
K>И вторая, это то, с какой легкостью народ пишет многопоточные программы, не сильно задумываясь о последствиях (тут тормозит, давай в отдельный поток, тут надо не зависнуть при записи в большой файл, а давай еще один поток).
Я наблюдаю обычно обратное.
K>Ну, вот судя по тому, сколько разных техник ты предложил, можно понять, что проблема существует, и что она довольно серьезная.
Проблем много всяких существует, в том числе и серьезных. Но это не повод лечить головную боль топором.
Здравствуйте, ksandro, Вы писали:
S>>Накладные расходы на всякую мелочёвку начинают отнимать неделю времени вычисления. K>Ок, я предположу, что ты у тебя действительно есть пример, где замена потоков на процессы может дать существенную просадку в производительности. Но все-таки хотелось бы большей конкретики?
Я не знаю, это зависит от ограничений, которые ты захочешь внести. Я не вижу большой разницы между потоками и процессами в смысле того, что я тут же сделаю общую память через mmap и синхронизацию на event'ах и буду писать так же, как и для потоков. Очевидно, однако, что это не даст никакой защиты, о которой ты думал.
K>>>Даже крутые гуру часто не могут написать, что-то более менее сложное без дедлоков и гонок. S>>Это только первые десять лет сложно. K>Больше десяти лет опыта, а все еще юношеский максимализм... Скажи еще, что у тебя вообще в коде багов не бывает.
Бывает, но я не помню, когда был последний, связанный с параллельностью. Не последние пару лет точно.
S>>Дедлоки и гонки не нужно выявлять тестированием, их нужно не создавать при проектировании. K>Круто, скажи еще, что у тебя все точно работает как спроектировал вначале после 10 лет поддержки и доработки.
У меня ни разу не было проблем переделать многопоточность. Не то, чтобы это часто требовалось, но тем не менее.
K>Вообще завидую людям, которые не работали с крыпными проектами, где много legacy, не сталкивались с индусским кодом.
Здравствуйте, ksandro, Вы писали:
K>Здравствуйте, Serginio1, Вы писали:
S>> Операция записи как раз не потокобезопасна. Пример из БД. Тебе нужно модифицировать набор записей в котором присутствует касаа. S>>Ту не можешь одновременно списать деньги, так как их может оказаться меньше. Но транзакция может и не завершиться если по каким то причинам эти деньги спасить нельзя. S>>И вторая транзакция должна дождаться, пока первая не закомитится или не откатится.
K>Ок, понял, только один вопрос, зачем здесь многопоточность? Если запись не идет параллельно, то зачем нужно много записывающих потоков, они будут толкаться на записи, и мешать друг другу. Запись транзакций в базу, как-раз отличный пример, где могопоточность не нужна, а неблокирующий ввод/вывод отлично справится (запись в базу не блокируется).
Угу если бы не многопоточность, базы бы еле шевелились. Запись идет параллельно если нет конкуренции, но если ты изменяешь одинаковые записи как например списание с кассы денег, товара со склада итд нужно контролировать остатки иначе продаж больше чем у тебя есть.
S>> Если ты используешь общие данные только на чтение, то и блокировки не нужныю K>ну вот почему бы их не скопировать во все процессы (будет работать быстрее), а если их много для копирования во все процессы, то можно разместить в shared memory? Тут потоки может и дают некоторое удобство, но ты всегда рискуешь, что кто-нибудь потом, своими шаловливыми рученками решит добавить апдейт данных. Лично я даже в случае потоков, постарался бы скопировать данные, чтоб они были локально у каждого потока.
Не будут они работать быстрее. Какой смысл? Для каждого запроса отдельный процесс? Ты будешь терять на межпроцессном взаимодействии.
Там же не просто память, но еще и сборщик мусора итд. Который тоже параллельно может работать и блокировать потоки для сборки мусора.
S>>>>Там где есть запись обычно работают блокировки ReadWriteLock K>>>Ужасно тормозная вещь, имеет смысл, толко когда у очень тебя много параллельных чтений и очень мало записей. S>> Так так в большинстве и существует. Берем ту же БД и транзакции. Там в большинстве блокировки именно на чтение
K>Ок, впринципе, допустим, тут действительно нужен такой паралеллизм. Много чтений, и редкие записи. Опять же, что мешает расшарить коннекшен базы данных между процессами, и так же синхронизировать его? K>Возможно в .Net нельзя использовать rw lock через shared memory? В линуксе это точно можно сделать.
Нужна не просто память. Про сборку мусора писал. В .Net есть домены. Есть какая то общая память для Dll.
Но какой толк от shared memory если ты от блокировок то не освобожден.
S>> Прежде чем говорить, ты бы пример привел где эти тормоза. Вот взаимодействие между процессами там дикие тормоза получишь.
K>Большинство потокобезопасных контейнеров содержат внутри себя блокировку, то есть чтение и записть в них идет последовательно. Есть правда еще lock free контейнеры, но судя по всем тестам, такие контейнеры выигрывают у обычных поткобезопасных в очень редких случаях. Моя практика показывает, что какой-нибудь хеш-мап, который расшаривается между потоками часто является узким местом, и в итоге производительность от многопоточности не увеличивается, а скорее проседает. Но тут надо конечно смотреть по задаче, вообще главное в многопоточных программах — это стараться как как можно меньше расшаривать данные между потоками. Поэтому таких контейнеров надо стараться избегать. Но я признаю, что иногда это полезно. Но теоретически контейнер можно расшарить между процессами, поэтому многопоточность тут нужна скорее от убогости API процессов, с которыми это сделать сложно, чем именно от необходимости иметь именно потоки с полностью общей незазизенной памятью.
Но процессы тут тебе не помогу! Каждый процесс это отдельный поток! Ты ничего не выигрываешь! Только произрываешь на межпроцессном взаимодействии.
S>>Но если ты используешь асинхронность тот же JavaScript работает на одном потоке, но доступ к данным из разных задач не защищен. K>Да, но там не будет race condition и deadlock! Именно это ошибки, которые могут невыявиться тестированием, и именно от них надо защищать данные при паралеллизме. Там конечно тоже все не просто, но, тестирование по крайней мере позволяет эффективно отловить баги. То есть главная проблема многопоточности исчезает!
Тестирование так же поможет и при многопоточности! S>>Ничего не надо ставить если используешь только чтение. K>Уже ответил на это
S>> Еще раз в большинстве случаев происходит чтение данных , в большинстве случаев к базе данных. Нет проблем с многопоточностью. S>>Твои проблемы надуманы. K>Ну, у разных людей разные проблемы, если ты единственный разработчик на проекте, ты очень четко знаешь и следишь за тем, что может шариться между потоками а что нет, и все работает стабильно, то впринципе используй... все так делают, ничего ужасного в этом нет. Но, если у тебя вдруг ни с того ни с сего после долгой стабильной работы когда-нибудь выскочит race condition, можешь вспомнить обо мне
У тебя есть статические свойства. Соответственно доступ к ним и нужно контролировать. Проектируя классы ты уже заранее понимаешь где он используется и если есть запись из разных потоков готовишь потокобезопасный код.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, Sharowarsheg, Вы писали:
S>Я не знаю, это зависит от ограничений, которые ты захочешь внести. Я не вижу большой разницы между потоками и процессами в смысле того, что я тут же сделаю общую память через mmap и синхронизацию на event'ах и буду писать так же, как и для потоков. Очевидно, однако, что это не даст никакой защиты, о которой ты думал.
Ну, понимаешь, во первых ты это сделаешь явно. Во вторых скорее всего у тебя все таки будут какие-то классы и переменные которые будет находиться памяти процесса а не в shared memory. То есть когда кто-то посмотрит твой код, он сразу увидит этот mmap и скорее всего поймет, как надо с этим работать.
K>>Больше десяти лет опыта, а все еще юношеский максимализм... Скажи еще, что у тебя вообще в коде багов не бывает. S>Бывает, но я не помню, когда был последний, связанный с параллельностью. Не последние пару лет точно.
Возможно они просто еще не прояались
K>>Круто, скажи еще, что у тебя все точно работает как спроектировал вначале после 10 лет поддержки и доработки.
S>У меня ни разу не было проблем переделать многопоточность. Не то, чтобы это часто требовалось, но тем не менее.
Везет тебе...
Здравствуйте, ksandro, Вы писали:
S>>Я не знаю, это зависит от ограничений, которые ты захочешь внести. Я не вижу большой разницы между потоками и процессами в смысле того, что я тут же сделаю общую память через mmap и синхронизацию на event'ах и буду писать так же, как и для потоков. Очевидно, однако, что это не даст никакой защиты, о которой ты думал.
K>Ну, понимаешь, во первых ты это сделаешь явно.
Я не понимаю, что тебе мешает сделать так же явно, но на потоках?
K>Во вторых скорее всего у тебя все таки будут какие-то классы и переменные которые будет находиться памяти процесса а не в shared memory. То есть когда кто-то посмотрит твой код, он сразу увидит этот mmap и скорее всего поймет, как надо с этим работать.
То же самое будет и в потоках, будут какие-то переменные, которые по комплекту на поток. Если хочется явности, всегда можно завернуть потоки в классы, и поделить переменные на private/public, а свойства, которые property, обернуть в блокировки, где надо. Это ещё лучше, потому что с mmap надо что-то понимать и самому следить, а с классами за какой-то частью будет компилятор следить.
K>>>Больше десяти лет опыта, а все еще юношеский максимализм... Скажи еще, что у тебя вообще в коде багов не бывает. S>>Бывает, но я не помню, когда был последний, связанный с параллельностью. Не последние пару лет точно. K>Возможно они просто еще не прояались
Возможно, но переход от потоков к процессам дополнительной уверенности не даст.
K>>>Круто, скажи еще, что у тебя все точно работает как спроектировал вначале после 10 лет поддержки и доработки.
S>>У меня ни разу не было проблем переделать многопоточность. Не то, чтобы это часто требовалось, но тем не менее. K>Везет тебе...
Я, если честно, не понимаю вообще проблемы. Многопоточных конструкций не так много, довольно быстро нарабатывается штук пять шаблонов разных, потом выбирай подходящий да применяй, вот и всё.
Здравствуйте, Sharowarsheg, Вы писали:
S>Я не понимаю, что тебе мешает сделать так же явно, но на потоках?
А какой в этом смысл, то, что не сделано явно по умолчанию доступно из любого потока. Главная цель, чтобы, когда правишь уже существующий код, нельзя было изменить получить доступ из потока к тому, к чему доступ из потока быть не должно. Ну или хотя бы иметь возможность поизучав код довольно быстро понять, что эта штука локальна для потока, а эта может использоваться в разных потоках.
S>То же самое будет и в потоках, будут какие-то переменные, которые по комплекту на поток. Если хочется явности, всегда можно завернуть потоки в классы, и поделить переменные на private/public, а свойства, которые property, обернуть в блокировки, где надо. Это ещё лучше, потому что с mmap надо что-то понимать и самому следить, а с классами за какой-то частью будет компилятор следить.
Да, лучше, пока кто-то кривыми руками не поправит твой красивый код. А потом, еще кто-то и еще кто-то.
S>Возможно, но переход от потоков к процессам дополнительной уверенности не даст.
Да, по хорошему должно быть что-то похожее на реализацию многопоточности в языке Rust, ну или один поток. Процессы уверенности не дадут. Но они дают хотя бы минимальную защиту на уровне операционной системы.
S>Я, если честно, не понимаю вообще проблемы. Многопоточных конструкций не так много, довольно быстро нарабатывается штук пять шаблонов разных, потом выбирай подходящий да применяй, вот и всё.
Проблема в том, что ошибки крайне сложно выявить. А программы пишут далеко не всегда крутые специалисты, код не всегда хорошо структурирован и документирован. И вот ты делаешь маленькую простенькую оптимизацию во вспомогательном классе, который ни на что серьезное не влияет, но из-за оптимизации код в этом месте работает чуть побыстрее. И вдруг, ни с того ни с сего у тебя начинают проявляться race condition-ы оставленные индусами 5 лет назад вообще в другом модуле, который причем очень очень важен и 5 лет работал стабильно и тебе еще надо начальству объяснять, какого хрена ты сломал всю бизнес логику, ты ведь вообще не должен был ее трогать, она 5 лет работала, зачем ты ее сломал! И это еще хорошо, если они появляются до продакшена.
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, ksandro, Вы писали:
K>>Но меня реально поражают две вещи, первая: почему в мейнстрим языках нет никакой поддержки защиты данных от доступа из разных потоков на уровне компилятора, это же всегда неправильно работает.
НС>А как, по твоему, это должно выглядеть?
Ну, в каких нибудь с и с++ все должно быть так как и есть. Эти языки и должны работать на низком уровне. Но когда придумывали java и c# уже говорили о том, что эти языки создаются с учетом многопоточности.
Я не специалист по разработке языков программирования, но ИМХО понятие потока должно быть встроено в язык. Любая переменная созданная в рамках потока по умолчанию должна быть доступна только внутри этого потока. Если к переменной надо обратиться из другого потока, то при создании переменной должно быть явно указано, что она доступна из разных потоков. О деталях можно много говорить... Как по мне, то большую часть этих проверок реально реализовать в compile time, но что-то может придется делать и в runtime, ради безопасности кое какой производительностью можно и пожертвовать.
K>>И вторая, это то, с какой легкостью народ пишет многопоточные программы, не сильно задумываясь о последствиях (тут тормозит, давай в отдельный поток, тут надо не зависнуть при записи в большой файл, а давай еще один поток).
НС>Я наблюдаю обычно обратное.
Ну, может поэтому тебе и так нравятся потоки
K>>Ну, вот судя по тому, сколько разных техник ты предложил, можно понять, что проблема существует, и что она довольно серьезная.
НС>Проблем много всяких существует, в том числе и серьезных. Но это не повод лечить головную боль топором.
Ну, почему, нет параллелизма нет ни гонок ни дед локов. Могу точно сказать, что часто многопоточность встраивают туда, где спокойно можно обойтись без нее.
То есть, я считаю что перед тем как использовать параллельное исполнение, надо 10 раз подумать, а так ли оно мне нужно...
Здравствуйте, ksandro, Вы писали:
S>>То же самое будет и в потоках, будут какие-то переменные, которые по комплекту на поток. Если хочется явности, всегда можно завернуть потоки в классы, и поделить переменные на private/public, а свойства, которые property, обернуть в блокировки, где надо. Это ещё лучше, потому что с mmap надо что-то понимать и самому следить, а с классами за какой-то частью будет компилятор следить. K>Да, лучше, пока кто-то кривыми руками не поправит твой красивый код. А потом, еще кто-то и еще кто-то.
Так можно любой код запороть. Пишешь CLI/HLT в середине да и всё.
S>>Я, если честно, не понимаю вообще проблемы. Многопоточных конструкций не так много, довольно быстро нарабатывается штук пять шаблонов разных, потом выбирай подходящий да применяй, вот и всё.
K>Проблема в том, что ошибки крайне сложно выявить. А программы пишут далеко не всегда крутые специалисты, код не всегда хорошо структурирован и документирован.
Это проблема, которая решается административными средствами, или рыночными, а не программными.
K>И вот ты делаешь маленькую простенькую оптимизацию во вспомогательном классе, который ни на что серьезное не влияет, но из-за оптимизации код в этом месте работает чуть побыстрее. И вдруг, ни с того ни с сего у тебя начинают проявляться race condition-ы оставленные индусами 5 лет назад вообще в другом модуле, который причем очень очень важен и 5 лет работал стабильно и тебе еще надо начальству объяснять, какого хрена ты сломал всю бизнес логику, ты ведь вообще не должен был ее трогать, она 5 лет работала, зачем ты ее сломал!
И эта тоже. Начиная от вопроса, зачем ты на самом деле лез в это говно без указания начальства, и заканчивая тем, почему ты работаешь там, где были индусы.
Здравствуйте, ksandro, Вы писали:
S>>Возможно, но переход от потоков к процессам дополнительной уверенности не даст. K>Да, по хорошему должно быть что-то похожее на реализацию многопоточности в языке Rust, ну или один поток. Процессы уверенности не дадут. Но они дают хотя бы минимальную защиту на уровне операционной системы.
В расте система безопасности — фикция. Т.е. она работает, но за пределами хэллоу ворда они предлагают весьма спорные решения, которые в рантайме стоят неслабо да и в понимании сути происходящего не добавляют. Их трюки конечно хороши, но ничего реально практически полезного не приносят. Да, еще, там альтернативное название функций — fun? фан ли? Яа пакеты почему-то называются ящиками.
В целом-то язык стройный, но при всех прочих неравных, это отвратительный языкмкоторый будет пудрить мозг своими захлестами в простейших ситуациях.
Более того, как только Servo станет однопроцессным — тогда можно будет поговорить о гарантиях. Но он изначально пессимизирует.
А достаточные гарантии безопасности как бы обеспечиваются в любом языке программирования, для этого не нужно ни специальных средств, ни специального языка.
Прошу понять правильно — их идея интересная, но забавно наблюдать их подходы для того что бы сломать их систему "borrowing". И оно на практике нужно ломать постоянно. Это в топку.
Здравствуйте, ksandro, Вы писали:
_>>Ну вот например при программирование на языке Rust, компилятор гарантирует тебе отсутствие таких проблем. K>Тут согласен! Я изначально не говорил что потоки не нужны вообще, но они не нужны в том виде, в каком они реализованы в майнстрим языках. К сожалению rust не майнстрим язык, на нем мало что пишут. Ну и я на практике с потоками на rust не игрался, поэтому не факт, что там не будет своих подводных камней. Я очень надеюсь, что потоки в rust окажутся действительно реализованными правильно, и это будет просто супер!
Потоки в Rust'е абсолютно обычные. Там идея совсем в другом. Rust не даёт тебе создать новые ссылки на какую-то память, если у тебя где-то в программе уже есть мутабельная ссылка на эту память. Поэтому просто физически невозможно получить большую часть проблем многопоточности. Точнее конечно же можно сломать все эти защиты, но для этого потребуется использовать блок unsafe...
_>>А таких "специалистов" лучше вообще не допускать до программирования. K>Ну, на первый взгляд всегда кажется, что у всех кругом кривые руки. А вот попробуй на практике поправить что-нибудь в legacy коде, который активно использует многопоточность...
В legacy никогда не копался. ) Ну не считая старых опенсорсных библиотек, которые надо было приспособить в свою программу.
_>>Т.е. я правильно понимаю, что ты предлагаешь использовать необходимость написания ненужного кода по организации междпроцессного взаимодействия в качестве инструмента исправляющего неумение некоторых программистов организовывать свой код? )))
K>Тут такое дело, что ИМХО 90% программистов не умеет организовывать многопоточный код. Вообще, если ты умеешь его организовывать, может напишешь, как это делать правильно, мне было бы очень интересно, так как я нормально с многопоточностью работать так и не научился. Если ты нашел способы, как избежать ошибок синхронизации, это будет просто супер!!! Только не надо маленьких примерчиков. Мне интересна такая ситуация, вот есть у тебя давно написанная кем-то большая программа с кучей кода, которая и еще использует разные библиотеки, тебе надо добавить в нее маленькую фичу, вроде делается все просто, вызвать пару функций, удалить пару элементов из одного контейнера, добавить в другой. Но потом понимаешь, что где-то там в дебрях этой проги используются потоки. Было бы очень интересно узнать можно ли как-то гарантированно проверить, что твои маленькие изменения не вызовут race condition?
Не, про работу с программой написанной в чужой архитектуре (причём ещё и возможно не верной) я тебе ничего сказать не могу. Я могу высказать экспертное мнение по выбору правильной архитектуры (которая не позволит в дальнейшем появляться подобным проблемам) при написание приложения с нуля. В этом у меня есть хороший опыт. Напишу немного об этом в следующем сообщение.
K>И да, в случае использования процессов тебе приходится потратить время на межпроцессное взаимодействие, да, есть накладные расходы на создание процесса, но ты получаешь хоть какую-то защиту от ОС от ошибок синхронизации. Реализация многопоточности в мейнстрим языках не просто не дает защиты отнеправильного использования потоков, но наоборот способствует и поощьряет его. K>Во многих случаях параллельное исполнение вообще не нужно, нажен просто неблокирующив ввод вывод, что вполне можно сделать без многопоточности. Это не очень просто, но все-таки не так опасно как работа с потоками.
Как раз неблокирующий ввод-вывод более опасен своей немасштабируемостью — будет у тебя подвисающее приложение при любых намёках на большие данные пользователя.
Здравствуйте, ksandro, Вы писали:
_>>Это ты тут свой опыт пересказываешь? ) Ну возможно стоило в начале ознакомиться с теорией вопроса (общепринятыми практиками решения этих проблем), а потом уже собственно кодить? ) K>В том числе и мой, но не только мой. Теория-то в общих чертах более менее понятна, но по многопоточности написано огромное количество книг и статей, и все продолжают писать и писать. Более того, регулярно выясняется что даже крутые гуру многопоточности в своих статьях таки делают ошибки. И как раз крутые гуру многопоточности всегда говорят, что многопоточность — это очень и очень сложная вещь. Реальные проблемы с многопоточностью обычно начинаются, код обрастает функциональностью, и его размер увеличивается его начинают править разные люди, пусть даже грамотные. K>Но вообще я буду рад, если ты приведешь подборку материалов про best practices в многопоточности, может там будет что-то новое для меня...
Вообще говоря ситуация совсем не такая. В реальности все эти проблемы имеют известные решения, в том числе и со 100% гарантией. Вся тонкость тут в том, что эти гарантии обычно не бесплатные. Поэтому основной задачей правильного архитектора является подбор инструментов адекватных требованиям задачи.
Например, нам достаточно сказать, что все данные нашей программы иммутабельны, как тут же получим полную защиту от почти всех проблем параллелизма. Правда при этом получим огромный расход памяти и невозможность реализовать некоторые алгоритмы, однако про все проблемы многопоточности сразу можно забыть. Примеры этого можно увидеть например в языке Хаскель (правда там это сделано не для решения проблем параллелизма, а совсем по другим причинам, но как следствие имеем и такой бонус) или в кластерном фреймворке Spark (тоже решает проблемы параллелизма, только на большем масштабе).
Для более адекватных приложений логичным выглядит применение модели акторов или CSP (это почти одинаковые концепции, с небольшими архитектурными различиями, которые надо подбирать под конкретное приложение). Во всех приличных языках можно найти реализации этих подходов в виде готовых библиотек. А в таких языках как Erlang и Go они вообще реализованы на уровне конструкций самого языка (правда у них там речь про зелёные, а не системные потоки, но это абсолютно никак не влияет на суть). В данном подходе у нас данные уже вполне изменяемые, но архитектура приложения гарантирует, что с одной областью память в один момент времени будет работать только один поток. Причём это гарантия не виде каких-то охранных блокировок, заставляющих другие потоки ждать доступа, а именно архитектурная — у потоков просто нет этих данных до нужного момента.
Ещё более низкоуровневая (но всё же чуть выше ручных мьютексов) абстракция взаимодействия потоков — это концепция promise/future. Она намного ограниченнее акторов, т.к. не подходит для сложных диалогов между потоками, но зато почти не имеет накладных расходов (по сути это просто автоматический мьютекс вокруг возвращаемого из потока результата). Стандартные реализации есть наверное во всех приличных языках.
Но и акторы и future не являются самым эффективным способом, если надо выжать из кода максимальную эффективность, т.к. не позволяют некоторые "опасные" сценарии. Для максимума надо писать ручной код, оптимизированный под конкретную задачу, с ручными блокировками в нужных местах. Причём в идеале это должны быть lock-free алгоритмы на базе атомарных CAS операций. И вот тут уже действительно нужен специалист по написанию параллельного кода, т.к. слишком легко сделать незаметную ошибку. Но это цена за максимальную производительность, которая нужна мягко говоря не всегда.
И напоследок ещё хочу отметить, что мы тут говорили именно о проблеме взаимодействующих между собой потоков. Потому как для случая независимых (пускай даже и работающих при этом на разных частях одного и того же блока памяти) потоков никаких проблем уже давным давно нет и подобный код спокойно генерируется автоматически (смотри тот же openmp и ещё множество подобных инструментов) с гарантией отсутствия ошибок.
_>>У процессов по сравнению с потоками есть только недостатки и ни единого достоинства. Если в задаче требуется использование общей памяти разными задачами, то тебе в любом случае понадобятся какие-то примитивы синхронизации. Что между процессами, что между потоками. И единственная разница будет в том, что в случае процессов тебе придётся написать ещё кучи глупого кода по организации общей памяти. K>Так или иначе, но для процессов, ОС дает какую-то гарантию от правки данных, которые не находятся в shared memory, в случае потоков (а точнее их реализации в большинстве языков), вся надежда только на внимательность. А если кода много, и его правят разные люди. То по закону мерфи кто-то обязательно рано или поздно напортачит с потоками.
Если в приложение используется многопоточность, но при этом не применялись вообще никакие правила её построения, то вполне возможно. Но зачем обсуждать такие печальные случай? Надо их просто не допускать изначально...
K>Я не говорю, что процессы — панацея. Но проблемы с потоками имеют свойство долго скрываться и вылезать в самый неудобный момент. K>ИМХО очень часто многопоточность используют не для паралеллизма для организации неблокирующего ввода вывода. А это лучше и проще делать в одном потоке.
У меня обратная точка зрения — утомило уже что из-за моды асинхронный ввод-вывод пихают везде, хотя в большинстве задач он менее эффективен, чем синхронный код в отдельном потоке.
Здравствуйте, Mystic Artifact, Вы писали:
K>>Да, по хорошему должно быть что-то похожее на реализацию многопоточности в языке Rust, ну или один поток. Процессы уверенности не дадут. Но они дают хотя бы минимальную защиту на уровне операционной системы. MA> В расте система безопасности — фикция. Т.е. она работает, но за пределами хэллоу ворда они предлагают весьма спорные решения, которые в рантайме стоят неслабо да и в понимании сути происходящего не добавляют. Их трюки конечно хороши, но ничего реально практически полезного не приносят.
К Rust'у конечно же можно придумать множество претензий. Но уж только не претензию про цену в рантайме. Такое может написать только человек абсолютно не разобравшийся даже в самых базовых концепциях языках.
Код там генерируется считай такой же как в C++, т.е. вообще без накладных расходов. А вся защита заключается в том, что компилятор просто не позволяет скомпилировать потенциально опасное приложение.
MA> Более того, как только Servo станет однопроцессным — тогда можно будет поговорить о гарантиях. Но он изначально пессимизирует.
Не понял, что ты хотел тут сказать.
MA> А достаточные гарантии безопасности как бы обеспечиваются в любом языке программирования, для этого не нужно ни специальных средств, ни специального языка.
Не, гарантии очень разные в разных языках. Но как всегда они не бесплатные. Только цена везде разная. Где-то мы расплачиваемся медленным кодом, отжирающим ресурсы, но при этом сохраняем простоту. А где-то (как в Rust'е) ценой является сложность языка.
MA> Прошу понять правильно — их идея интересная, но забавно наблюдать их подходы для того что бы сломать их систему "borrowing". И оно на практике нужно ломать постоянно. Это в топку.
Под ломать ты подразумеваешь unsafe код или что? )