Мы тут на работе организовали доморощенный курс по С++ и вообще программированию для людей без всякого компьютерного образования, которые хотят продвигаться.
Я подвизался его вести.
15 занятий уже прошло, всё более-менее нормально, и вот дошли до multithreading. И тут их прямо замкнуло. То есть, они понимают, что всё происходит одновременно, тут проблем нет. Но вот дошли до места, где один thread передает другому данные через BlockingQueue. Сначала они никак не могли понять, что функции одного и того же класса, могут бежать в разных threads. Наконец, после долгих раздумий, одна девушка говорит: а зачем вообще нужна эта очередь? Почему нам просто не бросить данные из одного thread в другой?
И это меня прямо-таки ввело в просветление. Это же просто коан, если задуматься. Хлопок одной ладони. Думал-думал, и говорю — но мы же ровно это и делаем с помощью очереди — передаём данные из одного thread другому! Но она этот ответ не приняла (и я сильно подозреваю, что и все остальные тоже). Зачем нужен промежуточный этап (очередь какая-то), если можно просто послать вот эту штуку (instance) вот оттуда вот сюда?
Тут я совсем надолго задумался, и в принципе, думаю до сих пор. По всей видимости, проблема в том, что люди, далёкие от этого дела, не понимают разницы между data и execution. Я попробовал объяснить это так: вот смотрите, thread — это инструкции, которые не то чтобы живут в CPU, а исполняются там одна за другой, а данные — это то, что живет в памяти, и не исполняется, а просто существует. Дело усложняется тем, что инструкции работают не просто так, а с этими самыми данным, при этом сами инструкции (не процесс их исполнения, а их материальное воплощение, запись о них, тоже живёт в памяти, просто в другом месте — ну, это ладно).
Кстати, пока писал, подумал: дело ещё усложняется и тем, что если смотреть на source code, то там действительно и переменные, и код функций, всё перепутано — какие-то функции в одном классе, какие-то в другом, при этом всё это может быть в разных threads без всякой системы. Какие-то переменные — члены одного класса, но изменяются из разных threads, другие принадлежат разным классам, но изменяются из одного; какие-то функции запускают новые threads — и так далее, до бесконечности.
В общем, вопрос получился чисто философский. В обычном мире вроде бы нет такой проблемы: все более или менее понимают разницу между объектами мира и процессами, которые выполняются во времени и работают с этими объектами. А в компьютере почему-то у людей случился сбой — тот же принцип оказался непонятным. Почему так, не знаю (хотя подозреваю, что правильный ответ в последней мысли предыдущего абзаца).
no subject
Date: 2020-07-20 09:34 pm (UTC)Преподаешь Питон - начинай с переменных, строк, циклов, функций и т.п. Память никого не волнует.
Преподаешь Эрланг - начинай с multithreading и pattern matching.
Преподаешь Лисп - начинай с рекурсии.
Преподаешь С++ - начинай с памяти, поинтеров, стека, хипа и т.п. Потому что первый же шаг - что такое operator new? почему нельзя вернуть референс на локальную переменную? почему моя переменная ещё жива, если я вызвал десять тысяч функций из этого места? - и всё пропало. В этом случае никого не волнует электричество и как устроен процессор, так что с этого начинать не надо.
no subject
Date: 2020-07-21 08:44 am (UTC)То есть, наверное, так: есть языки, где можно начинать не с глубины, а с поверхности. И есть языки, где никакой поверхности нет, начинать нужно сразу с глубины. Но рано или поздно до глубины всё равно придётся дойти. Ну или остановиться на каком-то промежуточном уровне - как ты пишешь, править заголовки, писать документацию.
no subject
Date: 2020-07-21 08:55 am (UTC)Но это уже зависит от целей учеников. Кстати, ученики подобрались очень усердные, делают домашние задания, честно стараются!
no subject
Date: 2020-07-21 09:34 am (UTC)