catpad: (Default)
[personal profile] catpad

Мы тут на работе организовали доморощенный курс по С++ и вообще программированию для людей без всякого компьютерного образования, которые хотят продвигаться.
Я подвизался его вести. 



15 занятий уже прошло, всё более-менее нормально, и вот дошли до multithreading.  И тут их прямо замкнуло. То есть, они понимают, что всё происходит одновременно, тут проблем нет. Но вот дошли до места, где один  thread передает другому данные через BlockingQueue. Сначала они никак не могли понять, что функции одного и того же класса, могут бежать в разных threads. Наконец, после долгих раздумий, одна девушка говорит: а зачем вообще нужна эта очередь? Почему нам просто не бросить данные из одного thread в другой?
И это меня прямо-таки ввело в просветление. Это же просто коан, если задуматься. Хлопок одной ладони. Думал-думал, и говорю — но мы же ровно это и делаем с помощью очереди — передаём данные из одного thread другому! Но она этот ответ не приняла (и я сильно подозреваю, что и все остальные тоже). Зачем нужен промежуточный этап (очередь какая-то), если можно просто послать вот эту штуку (instance) вот оттуда вот сюда?

Тут я совсем надолго задумался, и в принципе, думаю до сих пор. По всей видимости, проблема в том, что люди, далёкие от этого дела, не понимают разницы между data и execution. Я попробовал объяснить это так: вот смотрите, thread — это инструкции, которые не то чтобы живут в CPU, а исполняются там одна за другой, а данные — это то, что живет в памяти, и не исполняется, а просто существует. Дело усложняется тем, что инструкции работают не просто так, а с этими самыми данным, при этом сами инструкции (не процесс их исполнения, а их материальное воплощение, запись о них, тоже живёт в памяти, просто в другом месте — ну, это ладно).  
Кстати, пока писал, подумал: дело ещё усложняется и тем, что если смотреть на source code, то там действительно и переменные, и код функций, всё перепутано — какие-то функции в одном классе, какие-то в другом, при этом всё это может быть в разных threads без всякой системы. Какие-то переменные — члены одного класса, но изменяются из разных threads, другие принадлежат разным классам, но изменяются из одного; какие-то функции запускают новые threads — и так далее, до бесконечности. 


В общем, вопрос получился чисто философский. В обычном мире вроде бы нет такой проблемы: все более или менее понимают разницу между объектами мира и процессами, которые выполняются во времени и работают с этими объектами. А в компьютере почему-то у людей случился сбой — тот же принцип оказался непонятным. Почему так, не знаю (хотя подозреваю, что правильный ответ в последней мысли предыдущего абзаца). 




Date: 2020-07-18 12:53 pm (UTC)
From: [identity profile] gianthare.livejournal.com

Ну а чо, Эрланг, мессаджи, вот это все. Blocking queue это же implementation detail

Date: 2020-07-19 12:39 am (UTC)
From: [identity profile] catpad.livejournal.com
Ну так я и думаю, что здесь собака зарыта. Всё дело в синтаксисе: в Эрланге это и выглядит как process ! message - конечно же, вопросов у них бы здесь никаких не возникло. Но в Эрланге никому дела нет, как там внутри всё устроено.
А посмотри на его имплементацию на С++, и мы опять возвращаемся ко всяким очередям. И опять ступор...а...а...а почему бы не бросить этот мессадж прямо в этот процесс?

Date: 2020-07-19 05:02 am (UTC)
From: [identity profile] catpad.livejournal.com
Ну это же прямо Dining Philosophers problem.

Date: 2020-07-19 06:55 am (UTC)
From: [identity profile] gianthare.livejournal.com
ха-ха

Date: 2020-07-20 04:16 pm (UTC)
From: [identity profile] green-fr.livejournal.com
Отличная картинка!

Date: 2020-07-18 11:34 pm (UTC)
ak_47: (default)
From: [personal profile] ak_47
Интересное наблюдение. Это перекликается с моим собственным опытом обучения людей программизму. Я считаю что большинство таких известных трудностей - пойнтеры, рекурсия, multithread, и ещё несколько - все проистекают из непонимания или незнания основных принципов работы компьютера. Очень часто все эти неинтересные детали - регистры, память, стек и пр - проскакиваются при обучении как не особо важные для начинающих (вариант, для данного языка) или как advanced topics, которые надо изучать потом, а сейчас надо учиться писать Hello World с классами на Джаве.

В итоге человек сразу въезжает в функции, классы и ООП и даже что-то там понимает, потому что эти концепции логичны и отражают наше мышление. Но при этом в голове у человека создаётся искажённая картина того что на самом деле происходит в компьютере. Тем тяжелее потом перестроить своё понимание в свете новой информации. Пример с классами и тредами очень показательный. Я заметил что часто люди представляют себе классы (даже не объекты классов, а классы) как некие живые сущности со свойствами, методами и пр. Даже тот факт, что объектов класса может быть много часто удивляет. Потому что обычно-то как раз их не много, и зачастую вообще один, особенно при обучении.

По моему опыту с этим можно бороться только одним способом: объяснить как работает компьютер. Как процессор молотит числа, как работает стэк, доступ к памяти и пр. И уже на этих кирпичах строить более высокие абстракции. Как только люди это уясняют, то сразу пропадает вся мистика ООП и "сложность" мультитреда, например. Часто людям помогают аналогии из жизни. Можно представить треды как поваров, которые работают в одной кухне. Код это рецепт на стене, который все читают. А изменяемые данные это продукты в холодильнике. Повара не могут одновременно взять какой-то продукт и начать его резать, должны договориться кто берёт первым, кто вторым и т.д.
Edited Date: 2020-07-18 11:35 pm (UTC)

Date: 2020-07-19 12:13 am (UTC)
From: [identity profile] catpad.livejournal.com
Вот я читал ваш комментарий и прямо таял на глазах :)
Вы не поверите, насколько точно у нас совпадают мысли. Я тоже считаю, что это огромное зло, что людям часто преподают какие-то языки и при этом вообще ничего не объясняют, как это всё работает внутри. Особенно бессмысленно преподавать С++ и не показывать, что происходит в памяти.

Я встречал людей, которые программируют на С++ (и в общем, хорошо его знают), но не понимают, чем heap allocation отличается от stack allocation и что, например, если у тебя очень большой статический (или просто локальный) массив внутри функции, то это не повлияет на быстроту вызова функции. Или если ты хочешь менять поинтер внутри функции, то его надо передавать by reference (аргумент был: ну как - это же уже поинтер, зачем его by reference?)

Поэтому два самых первых занятия (4 часа, между прочим) я посвятил только разбору того, что происходит на стеке при вызове функций с разными типами аргументов (by value, by ref, by pointer); где и сколько живут локальные переменные; где хранятся разные типы данных в памяти; что есть program counter и как это всё связано. Потом ещё дал им домашнее задание нарисовать каждое изменение стека и все вообще все stack frames при работе рекурсивной функции factorial(3).

Но, как видите, всё равно не помогло. Хотя, я понимаю, что без этого всё было бы ещё гораздо хуже. По крайней мере, они теперь свободно оперируют понятиями scope, lifetime, heap и т.п.

Date: 2020-07-19 04:13 am (UTC)
From: [identity profile] yatur.livejournal.com
> если у тебя очень большой статический (или просто локальный) массив внутри функции, то это не повлияет на быстроту вызова функции

Ну, это вопрос философский. Если статический - не повлияет, я если локальный, да еще и проинициализирован, то надо же страницы виртуальной памяти выделять и заполнять их чем-то. Если ОЧЕНЬ большой - это займет время. А если ОЧЕНЬ-ОЧЕНЬ большой, то тупо стек кончится и время выполнения функции станет очень маленьким :)

Date: 2020-07-19 04:38 am (UTC)
From: [identity profile] catpad.livejournal.com
Ха-ха, ну, так далеко мы не заходили, конечно.
Я им показывал пример именно static, так что с этой точки зрения всё чисто.
Не думаю, что им вообще нужно знать про виртуальную память.

Date: 2020-07-19 02:20 pm (UTC)
From: [identity profile] yatur.livejournal.com
Да, пожалуй. Если начинать впихивать всё сразу, в голове образуется каша.

Date: 2020-07-20 08:32 am (UTC)
ak_47: (default)
From: [personal profile] ak_47
Даже самое хорошее объяснение не сразу усваивается. Но это дело практики и времени. Даже если человек не запомнит все нюансы, то хотя бы будет знать где копать если понадобится.

У меня вообще сложилось такое убеждение, что языки стоит преподавать где-то в самом конце обучения на CS. Как у врачей специализация. А до этого то, что общее между всеми языками.

Date: 2020-07-20 04:20 pm (UTC)
From: [identity profile] green-fr.livejournal.com
Ой, сложная тема. Нам может казаться, что так правильно, потому что нам так преподавали. Но при этом нам не рассказывали, как работают регистры на уровне электричества (ок, лично мне рассказывали, но мы не пытались хотя бы сами генерировать своё электричество) - с чего начинать? Цивилизация продвинулась так далеко. что очень сложно добежать до её передового края, если начинать с самого-самого начала. Ну или добежишь в какой-то крайне тонкой области. Сложная тема...

Date: 2020-07-20 09:34 pm (UTC)
From: [identity profile] catpad.livejournal.com
Нет, в данном случае не очень сложно. Начинать нужно с необходимого.
Преподаешь Питон - начинай с переменных, строк, циклов, функций и т.п. Память никого не волнует.
Преподаешь Эрланг - начинай с multithreading и pattern matching.
Преподаешь Лисп - начинай с рекурсии.
Преподаешь С++ - начинай с памяти, поинтеров, стека, хипа и т.п. Потому что первый же шаг - что такое operator new? почему нельзя вернуть референс на локальную переменную? почему моя переменная ещё жива, если я вызвал десять тысяч функций из этого места? - и всё пропало. В этом случае никого не волнует электричество и как устроен процессор, так что с этого начинать не надо.

Date: 2020-07-21 08:44 am (UTC)
From: [identity profile] green-fr.livejournal.com
Да не знаю. Я смотрю на свой любимый MatLab - вот уж где, кажется, вообще ничего знать не надо. А потом я смотрю на грабли, на которые наступают мои коллеги, потому что они не знают, что такое многопоточность, как выделяется память, как устроен процессор... Выбор языка помогает скрыть какие-то детали на первоначальном уровне. Но потом-то всё равно в какой-то момент нужно понимать, как что работает. В MatLab нет указателей. Но есть понятие class handle или class instance, объекты которых работают именно как переменные, переданные by ref или by val. Ну вот и как объяснять коллегам разницу, если они не понимают, как устроена память, как там лежат переменные?

То есть, наверное, так: есть языки, где можно начинать не с глубины, а с поверхности. И есть языки, где никакой поверхности нет, начинать нужно сразу с глубины. Но рано или поздно до глубины всё равно придётся дойти. Ну или остановиться на каком-то промежуточном уровне - как ты пишешь, править заголовки, писать документацию.

Date: 2020-07-21 08:55 am (UTC)
From: [identity profile] catpad.livejournal.com
Ну да, тут я полностью согласен. С чего ни начинай, всё равно в какой-то момент придётся дойти до устройства компьютера.
Но это уже зависит от целей учеников. Кстати, ученики подобрались очень усердные, делают домашние задания, честно стараются!

Date: 2020-07-21 09:34 am (UTC)
From: [identity profile] green-fr.livejournal.com
А вот здесь у меня - зависть, лютая зависть...

Date: 2020-07-19 01:15 am (UTC)
From: [identity profile] mishafurman.livejournal.com
Пример из обычной жизни: два человека разговаривают (обмениваются сообщениями) по телефону.
Телефонная линия, провода вроде как общие.
А без протокола (типа "да", "ты меня слышишь?" и.т.д - или "перехожу на прием".) ничего не получится - они либо будут говорить одновременно - и не слушать друг друга, или оба молчать: слушать.

Date: 2020-07-19 03:03 am (UTC)
From: [identity profile] us-retired.livejournal.com
Хм но очередь отнюдь не обязательна, от задачи же зависит. Если producer-consumer, то да. Но это ж не единственный паттерн.

Да и вообще, будьте проще. 90% задач решаются без параллельности. У начинающего часто создается ощущение, что если че медленно, надо распараллелить, вместо того, чтобы сначала подумать о другом подходе.

Да и вообще, ну зачем рядовому необученному C++? Зачем это людям без компьютерного образовния?
Edited Date: 2020-07-19 03:06 am (UTC)

Date: 2020-07-19 04:41 am (UTC)
From: [identity profile] catpad.livejournal.com
Они хотят переходить на работу в R&D. Хотя бы на простые задачи. Не уверен, что получится, но пусть попробуют.

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

Date: 2020-07-19 04:46 am (UTC)
From: [identity profile] us-retired.livejournal.com
В R&D пишут на плюсе? Простые задачи? ;-)

Date: 2020-07-19 04:49 am (UTC)
From: [identity profile] catpad.livejournal.com
Простые теперь всё больше на Питоне, но и на С++ бывает.

Date: 2020-07-19 04:50 am (UTC)
From: [identity profile] us-retired.livejournal.com
Ну вот Питон куда как проще и ближе народу.

Date: 2020-07-19 06:31 am (UTC)
From: [identity profile] aleks-visero.livejournal.com
В обычном мире тоже есть такая вещь. Например, использование мыла для прокладки кабеля.

Date: 2020-07-19 02:22 pm (UTC)
From: [identity profile] e2pii1.livejournal.com
С++ для "людей без всякого компьютерного образования" - думаю, плохой выбор языка.
С++ решает те проблемы, о которых эти люди вообще не понимают, что такие проблемы существуют и как/где/откуда могут возникнуть. Т.е. эти люди могут "вызубрить" правила языка, но для глубокого понимания нужен реальный серьёзный опыт разработки.

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

А зачем, если не секрет ?

Date: 2020-07-20 01:30 am (UTC)
From: [identity profile] catpad.livejournal.com
Не секрет, я же и говорю: люди хотят продвигаться, сменить департмент на более интересный.
А выбор языка обусловлен исключительно рабочей необходимостью. У нас 90% проектов на С++.

Date: 2020-07-20 02:03 am (UTC)
From: [identity profile] e2pii1.livejournal.com
Это японщина какая-то - возьмём любого выпускника с любым базовым образованием и выучим чему угодно :-)
Почему не хотят профильных специалистов нанять ?
Почему им не продвигаться там где у них уже есть опыт и знания ?

Date: 2020-07-20 09:36 pm (UTC)
From: [identity profile] catpad.livejournal.com
Ну почему японщина. Во всём мире это называется "повышением квалификации". Параллельно с наниманием профильных специалистов, конечно.

Date: 2020-07-19 02:36 pm (UTC)
From: [identity profile] e2pii1.livejournal.com
C multithreading вся проблема в синхронизации процессов/threads: как организовать так чтоб двое друг-другу не мешали, и чтоб получатель не лез в данные до того как их отправитель приготовил (и это сложная computer science, а не C++, проблема).

Так то данные и "бросaть" не надо: доступ то к ним у обоих есть.

Date: 2020-07-20 01:31 am (UTC)
From: [identity profile] catpad.livejournal.com
В этом в общем-то и суть всего поста: они не понимают разницы между доступом к данным и процессом, который к ним хочет доступиться.

Date: 2020-07-20 01:59 am (UTC)
From: [identity profile] e2pii1.livejournal.com
Пример с поварами выше в комментах - хорошее объяснение.
Но всё равно, одно дело понимать в чём тут проблема - а другое дело суметь аккуратно проблемы синхронизации решать не имея хорошего базового CS-образования. В последних версиях STL есть поддержка параллельности, что может несколько облегчать это пряча детали реализации, но всё равно я не уверен насколько новички-неспециалисты могут тут справиться.

Date: 2020-07-20 02:17 am (UTC)
From: [identity profile] e2pii1.livejournal.com
Помню в юности ранней, сидели мы на многопользовательской PDP, и админ там закрыл возможность копировать файлы между разными аккаунтами. Но там в системе было несколько "системных флагов" к которым был доступ (но не "семафоров" т.е. без всякой системной синхронизации). Я тогда додумался как копировать файлы синхронизируя с помощью 2х флагов (это частный случай когда возможна синхронизация без внешнего синхронизатора): один процесс-посылатель кладёт байт на остальные флаги и поднимает свой флаг "готово", а 2й процесс-получатель забирает данные и потом опускает 1й флаг и поднимает свой 2й флаг "принял, продолжай".

Date: 2020-07-20 06:54 am (UTC)
From: [identity profile] mopexod.livejournal.com
Я уже дня два собираюсь написать... Тут уже всё написали, что и я хотел.
Это, на мой неискушённый взгляд, некоторая утопия - взять человека без компьютерного образования, и попытаться обьяснить ему проблемы распараллеливания.
Или как написали выше - начинать с азов строения компьютера. Или такой язык, который прячет все детали от программиста, включая проблемы распараллеливания.
Но "короткий курс по С++" для последующей профессиональной работы - фу-фу-фу.

Date: 2020-07-20 08:12 am (UTC)
From: [identity profile] catpad.livejournal.com
Ну, тут с меня спроса нет: я начальство предупредил, что эти люди будут в лучшем случае исправлять по одной-две строчки в коде, а скорее всего конфигурировать какие-то конфиг.файлы.
Собственно, оба предсказания сбылись: кое-кто уже изменил две строчки в одном проекте, а кое-кто отконфигурировал файлы :)
Но иметь общее представление о происходящем совсем неплохо.

Date: 2020-07-20 08:36 am (UTC)
From: [identity profile] mopexod.livejournal.com
Это сейчас конфиг файлы. А чуть потом - строчка в резюме о курсах с++ и опыте работы!

Про то, что общее представление - неплохо, это как "не бывает лишних знаний". Вроде, оно и правда, но какая-то не очень полезная :)

Date: 2020-07-20 08:54 am (UTC)
From: [identity profile] catpad.livejournal.com
Ну да, я и не сомневаюсь, что так оно и будет :)
Page generated Feb. 6th, 2026 07:08 am
Powered by Dreamwidth Studios