Pull to refresh

Comments 50

Интересно, конечно, автор сравнивает две картинки и говорит, что go и goto на картинках похожи )
Возможно, я не уловил метафоры автора:
тогда среда выполнения… роняет ошибку на пол
применительно к языку Golang, среда роняет весь процесс выполнения программы и никуда ошибка не потеряется. Чтобы этого не случилось, нужно все подпереть recover. Так что, тут не совсем понятно в чем сложность обработки ошибок в горутинах.

Так речь то не только о языке Go (видимо я зря суслика на обложку поставил, и ввел всех в заблуждение), а об императивных языках, в которых описанные недостатки стоят в полный рост.

Но ведь это не так.
В данный момент меня больше интересует Golang, поэтому спрошу: в чем проблема контроля ошибок в горутине?
Если не использовать defer recover, то ошибка остановит и главный поток, поэтому она никуда незаметно не денется.
Если нужен recover, то ошибку можно отправить в канал или сохранить в лог и продолжить дальше, т.е. использовать ровно тот же подход к контролю ошибок, как и в главном потоке выполнения программы.
В Go в стандартной библиотеке есть WaitGroup. По смыслу тоже самое, можно подождать пока все горутины отработают.

Более того, есть golang.org/x/sync/errgroup, через который можно запускать горутины, и оно ещё ошибку вернет, если она возникла у какой-то из выполняемых в группе горутин.

Так себе. Nathaniel набрасывает на Go исключительно для громкого заголовка. Этот архитектурный астронавт накидывается на pthread_create, std::thread, ключевое слово go и забывает, что это базовые примитивы с которых начинается многопоточность.


Дальше описывает стандартные проблемы многопотоного программирования, такие как обработка ошибок и исключений в другом потоке. Рассуждая о потоке выполнения, он все сворачивает до fork–join, т.е. полностью забивает на модели concurrently, например акторы и CSP.

Не забивает, а изолирует в питомнике. Внутри питомника уже живут эти акторы.

С точки зрения Joel Spolsky, отраженной в его статье, чем проще, тем лучше (и я согласен), но асинхронное программирование таки часто необходимо, и Натаниель пытается сделать его значительно проще.


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


Заголовок действительно громковат, но это же не строгая научная статья в конце концов.

Так и решения то стандартные. Открывая Timeouts and cancellation for humans видим Cancellation token. Только пробрасывается скрыто:


You should think of with open_cancel_scope() as creating a cancel token, but it doesn't actually expose any CancelToken object publically. Instead, the cancel token is pushed onto an invisible internal stack, and automatically applied to any blocking operations called inside the with block. So requests doesn't have to do anything to pass this through – when it eventually sends and receives data over the network, those primitive calls will automatically have the deadline applied.

Только зачем он пишет о превосходстве своего решения на Python над примитивами из C#, Go. Примитивы на то и примитивы, что на базе них можно сделать высокоуровневые абстракции.

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

UFO just landed and posted this here

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

Автор статьи не понимает принципиальной разницы между параллельностью и конкурентностью.
Это легко понять сразу из факта того что вместе поставлены
go myfunc(); — это об конкурентности
pthread_create — это об параллельности

Автор говорит о потоке исполнения и многозадачности. Крутятся ли задачи на одном ядре или на разных в данном контексте не имеет значения. Ну а stackfull сопрограммы и системные потоки семантически одно и то же.

Да, я даже хотел эту мысль написать в примечаниях к статье, но подумал, что это достаточно очевидно.

Вы переоцениваете местную аудиторию.)

А есть где-то ещё согласные именно с такой «принципиальной разницей» между этими терминами, как вы утверждаете?
А то, если посмотреть на типовые утверждения вида

> Concurrency means multiple tasks which start, run, and complete in overlapping time periods, in no specific order. Parallelism is when multiple tasks OR several part of a unique task literally run at the same time, e.g. on a multi-core processor.

то и go myfunc(); не запрещает запустить на другом ядре и работать с непересекающимися данными, и нитка по pthread_create() не обязана выполняться на другом ядре (и на одноядерной таки будет конкурировать с текущей)…
Принципиальная разница в том, разделяют задачи общие данные или нет. Если не разделяют, то при отсутствии свободных потоков их можно выполнить последовательно в вызывающем потоке. Если разделяют — например, в фоновом режиме отсылают снимки экрана в АНБ — то нет.
Разделяют в каком смысле?

Если разделяют с одновременным доступом только на чтение, то и пусть выполняются и последовательно, и параллельно. За когерентность пусть аппаратура кешей/памяти отвечает.

А доступ на одновременную запись должен быть запрещён строго хоть компилятором, хоть средой исполнения. А хоть и вольно/нестрого – это на усмотрение разработчика и в соответствии правилами/ограничениями языка.
Автор воспринимает это как разные возможности в асинхронном подходе, которые лежат «под капотом». Он вообще рассматривает другую проблему. Пришел синтаксический сахар, который все равно не решает проблему контроля flow.

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

Спасибо за перевод.

Автор — клоун, я даже дочитать этот опус не смог. Коллеги выше уже отметили нулевые знания в области (непонимание concurrency, parallelism, CSP), так ещё и полное профанство в языке Go (15-минутный go tour очевидно не пройден).

Даже лень комментировать этот бред: «go похож на goto (нет), поэтому они одинаковые (нет)», "'go' ломает исключения, а без них ошибки не обработать" (исключений в Go нет), «каналы ещё не придуманы и не существуют», «мифические абстракции ломаются (автор не знает какие абстракции есть в Go, но уверен что они ломаются)», «go ломает автоотчистку ресурсов» (её в Go тоже нет, но автор и этого не знает).

Немного питонирую и могу понять, какую проблему автор «героически» решает. Но это проблема именно языка Python (и многих других к сожалению), а Go с его «go» спроектирован на базе решения этой проблемы и просто иммунен к ней (но не ко многим другим к сожалению).

Я, как переводчик, вероятно ввел всех в заблуждение, поставив на обложку аватар языка Golang, в то время как автор имеет в виду Go-подобные выражения в других языках. Просто в языке Го эта концепция доведена до совершенства, и многие описанные проблемы решены или почти решены, но в остальных мейнстримновых языках — так как они изначально создавались синхронными, а параллельность прикручивали сбоку, потому как мода пошла — все описанные проблемы присутствуют

В поздних 60-х

Такие вещи всё же стоит вычитывать после гугл транслейта…

Мне обидно слышать ваше предположение, так как в авто-переводчике (deepl) я переводил только отдельные слова, если не знал их значение.

Даже ухитрившись решить все эти проблемы, теперь в программе две системы обработки ошибок.

Увы, такие вещи всё равно просачиваются в текст.

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

Поглядев из любопытства на историю версий Trio, обнаружил там любопытную запись:

Trio 0.6.0 (2018-08-13)
Features: Add trio.hazmat.WaitForSingleObject() async function to await Windows handles.

Выглядит так, что от низкоуровневых примитивов, ещё и привязанных к платформе, автор отказаться так и не смог.

Ну так он их абстрагирует за фасадом питомников, для этого библиотеки и фреймворки и существуют, кажется.

Меня смутило слово features в описании и тот факт, что в ранних версиях библиотека без этого вполне обходилась. Я вообще раньше думал, что Питон — достаточно высокоуровневый язык для того, чтобы ничего не знать о примитивах Windows.

Ожидание события и прочие блокировки, как по мне — больше тянет на goto в параллельном программировании, чем создание новых потоков. Ну и тут говорят, что таки есть нюансы.
trio.hazmat is Trio’s “hazardous materials” layer: it contains APIs useful for introspecting and extending Trio. If you’re writing ordinary, everyday code, then you can ignore this module completely. But sometimes you need something a bit lower level.

trio.readthedocs.io/en/stable/reference-hazmat.html

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

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

[i]Корневая идея такова: каждый раз, когда поток выполнения разделяется на несколько параллельных путей, мы должны убедится, что они соединятся снова.[/i] — я в основном занимаюсь программированием микроконтроллеров: значительная часть потоков создаются и далее взаимодействуют или не взаимодействуют между собой исходя из конкретной задачи. У части задач может и не быть конца, как такового. Питание есть — задача работает.

[i]Если вы не используете Assembler [2], классический, неограниченный goto ушёл в прошлое.[/i] — довольно странное суждение: в общем случае ассемблерные коды мало отличаются от кода на других языках: люди себе не враги, чтобы писать такой код, который потом еще расхлебывать придется.
Не вижу в статье упоминания реактивных стримов.

Первый раз такое слышу, киньте пожалуйста ссылку на хорошее описание.

А ещё есть отличное решение от Интел, где все эти «питомники» (aka task_arena) как раз используются в качестве «чёрного ящика»

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

Чтобы не использовать GO нужны мощные BREAK и CONTINUE позволющие выбрать на какой из нескольких уровней вложенных циклов нужно выйти, а без этого приходиться использовать GO.
За что минус то?
Не во всех языках есть break и continue с выходом на выбранный уровень цикла.
Можно конечно, извратнутся присваивая некоторую переменную указывающую на какой уровень цикла нужно выйти, и в начале каждого цикла проверять значение, но это изврат.

Что break, что continue не умеют запускать асинхронную сопрограмму, в отличие от go. Следовательно, go нельзя заменить ни на break, ни на continue.

в с++ «менеджеры контекста», т.е. деструкторы отлично работают с goto. и с потоками (го). но судя по этой длинной статье в языках без деструкторов тяжело

В Питоне тоже есть деструкторы, и менеджерами контекста они не являются...

Если я вызову некий библиотечный метод который запскает дочерние такски через «asyncio.ensure_future», этот nursery как-то их отловит?

Насколько я знаю — нет. Все параллельные задачи надо запускать через объект питомник (можно также питомник в питомнике)

Интересная статья. Интересная философия. Интересная реализация, но…
Итак, это была теория. А как это работает на практике?

И дальше пустота!
И этим всё сказано — в статье явно не хватает примеров, особенно во второй её половине и тем более в конце — откуда и взята эта цитата. Практика то и не описана вовсе? Много рассуждений о попыток сравнения (увы абсолютно пустых и неконкретизированных попыток сравнения), а реальных примеров нет. Код очень скуден — доманстрация механизма напрочь отсутсвует. В начале стать есть интересные диаграммы — но дальше — идёт достаточной сухой текст почти без примеров и демострации как это всё работает. Есть критика других яызков — но нет примеров, показывающих их проблему (не говоря уже о том, что нет примеров, показывающих как эту проблему решают на этих языках — чтобы показать что решение на Trio действительно выглядет лучше). Особенно много отсылок к горутинам языка Golnag, но нет ни одного примера проблематики именно на Golang. Как и практически нет сравнения с языокм Erlnag/Elixir — считающимися практически эталоном организации надёжного и масштабируемого асинхронного выполнения кода. Увы, нет, сравнения с новомодным языком Kotlin — а там тоже есть интересная логика распараллеливания через корутины.
Что это? Вина переводчика? Ведь в ссылкам на англоязычный ресурс примеров куда больше (хотя — как мне показалось — они сумбурны и так же — это просто код на Python, без сравнения с другими языками). Да и ссылки на цельный оригинал нет? Что это — перевод-сборная солянка? В общем — любопытная технология — но преимущества показаны не так хорошо, чтобы всецело следовать данному подходу и применять в Python, не говоря уже о том, чтобы вызвать желание портировать библиотеку на другие языки программирования! Хотя, как идеология для новых языков или развития существующих языков со слабой/отсутствующей поддержкой управляемой асинхронности — такая философия может быть полезна к применению при развитии архитектуры этих языков. Но, как уже сказал, сила статьи недостаточна, чтобы сразу браться за внедрение данного подхода в ядро языка или хотя бы в состав базовых библиотек.

Действительно, в этой статье примеров кода маловато, но два примера с обьяснениями можно посмотреть в исполнении автора на youtube, также в блоге автора есть статьи, где примеров с искользованием Трио побольше, вот например, или вот статья про проблемы асинхронного программирования (тоже с большим количеством примеров), причем все эти ссылки есть в переведенной статье.


А данная статья упирает на логическо/историческое объяснение авторского решения, и примеры даны минимальные, чтобы просто объяснить суть. И мне кажется, это хорошо, чтобы не отвлекатся слишком сильно на погружение в дебри кода.


Да и ссылки на цельный оригинал нет?

В самом верху статьи, возле плашки "перевод" — кликните по имени автора, и попадете на оригинальную статью.


ни одного примера проблематики именно на Golang. Как и практически нет сравнения с языокм Erlnag/Elixir

Да, это слабое место статьи.


нет примеров… чтобы показать что решение на Trio действительно выглядет лучше

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


P.S. Спасибо за подробный комментарий

Сравнения с новомодным Kotlin нет, потому что заметка Натаниэля вышла до релиза котлиновской библиотеки корутин. И наоборот, в каждом своём выступлении руководитель разработки котлиновских корутин Роман Елизаров ссылается на пост в блоге Натаниэля. Тут, например.

Заметка Натаниэля, с моей точки зрения, ценна тем, что тут автор чётко формулирует новую концепцию, опуская детали реализации и другие несущественные подробности. С деталями реализации можно познакомиться, перейдя по ссылкам на страничке-агрегаторе.
Sign up to leave a comment.

Articles