LINUX.ORG.RU

Вопрос про многопоточность

 , ,


0

1

Лисп тут собственно, не при чем, просто пример на лиспе, а в вопросе важна реализация. Я не уверен, что в других ЯП также все реализовано.

Я не знаю низкий уровень, к сожалению, поэтому возникает путаница, по вопросам реализации.

Допустим, есть вот такой вот кодик

(define function (lambda() 'some-code))
(define foo function)
(define bar function)
В данном коде все 3 имени указывают на одну и ту же структуру в памяти. Собственно, при вызове любого имени, function, foo или bar, будет выполняться один и тот же код, физически.

Вопрос следующий. Допустим у нас 2 потока на 2-х разных процессорах. Можно ли, чисто физически, одновременно из этих 2-х процессов одновременно дернуть функцию function, например, через разные имена, допустим,из одного потока по foo, а из другого по bar? Чтобы оба вызова были действительно одновременны в реальном времени. Если у нас конкурентность, понятно, что это можно сделать, поскольку, у нас в риал-тайм происходит либо один либо другой вызов. А вот если будет реальная параллельность? Это же невозможно, или как?


Можно ли, чисто физически, одновременно из этих 2-х процессов одновременно дернуть функцию function, например, через разные имена, допустим,из одного потока по foo, а из другого по bar?

Конечно можно, и даже через одно и тоже имя.

Это же невозможно, или как?

Возможно. Не пойму, что тебя смущает?

no-such-file ★★★★★ ()

«абсолютная» одновременность не существует. Задай относительно чего ты измеряешь одновременность. Например, отнсоительно изменения какого-то общего состояния (переменной? они в твоем лиспе существуют?)

stevejobs ★★★★☆ ()
Ответ на: комментарий от no-such-file

и даже через одно и тоже имя.

Да, понятно, это я для наглядности наклепал 3 имени, может зря, только запутал.

Не пойму, что тебя смущает?

Да хрен его знает, как то не укладывается в голове, это выглядит так же, как если бы два человека пили одну и ту же воду одновременно. Не пойму, что-там за магия:)

phill ()
Ответ на: комментарий от stevejobs

«абсолютная» одновременность не существует. Задай относительно чего ты измеряешь одновременность. Например, отнсоительно изменения какого-то общего состояния (переменной? они в твоем лиспе существуют?)

Да, концептуально, function, foo и bar и есть переменные. Ну пусть будет так.

set=function(x){
   a=x
}
И теперь мы из 2-х потоков на разных процах вызываем вот так:
//1-й поток
set("foo")
//2-й поток
set("bar")
Очевидно же, что у нас будет либо а=«foo», либо a=«bar».

phill ()
Ответ на: комментарий от phill

как то не укладывается в голове, это выглядит так же, как если бы два человека пили одну и ту же воду одновременно

«Пить воду» это когда два процесса к одной переменной обращаются. А выполнять функцию - это как два человека одновременно читают одну и ту же инструкцию по бумажке и выполняют.

amomymous ★★★ ()
Ответ на: комментарий от amomymous

Но ведь функция представлена в данном случае одной и той же структурой. чтобы ее выполнить ее нужно сначала считать из памяти. А разве могут 2 разных процесса иметь одновременный (в реальном времени) доступ к одной и той же области памяти?

phill ()
Ответ на: комментарий от phill

А разве могут 2 разных процесса иметь одновременный (в реальном времени) доступ к одной и той же области памяти?

Чтобы выполняться одновременно, они должны находиться на разных процессорах. У каждого процессора L1d кэш свой, так что проблем с одновременным чтением не возникнет.

i-rinat ★★★★★ ()
Ответ на: комментарий от amomymous

когда два процесса к одной переменной обращаются

Ну хорошо, а если 2 процесса к одной переменной обращаются одновременно — это невозможно, при реальной одновременности?

phill ()
Ответ на: комментарий от i-rinat

У каждого процессора L1d кэш свой, так что проблем с одновременным чтением не возникнет.

Ну а если предположить, чисто гипотетически, что кеша нет, тогда бы возникали?

phill ()
Ответ на: комментарий от phill

тогда бы возникали?

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

i-rinat ★★★★★ ()
Ответ на: комментарий от phill

кеша нет, тогда бы возникали?

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

no-such-file ★★★★★ ()
Ответ на: комментарий от no-such-file

А строки?

(define function "(lambda() 'some-code)")
(define foo function)
(define bar function)
;1-й процесс
(eval foo)
;2-й процесс
(eval bar)

phill ()
Ответ на: комментарий от phill

Точней так

;1-й процесс
((eval foo))
;2-й процесс
((eval bar))
но не суть

//fixed

phill ()
Ответ на: комментарий от no-such-file

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

И, кстати, не понятно, как процессор будет парсить уже скомпиленный код, чтобы выделить эти куски, ведь эта ф-ция представлена в памяти уже какой-то фигней типа 101010101110101?

phill ()
Ответ на: комментарий от phill

не понятно, как процессор будет парсить уже скомпиленный код, чтобы выделить эти куски

Никак, процессор не знает ни о каких функциях и строках, он просто исполняет команды.

А строки?

Каждая «строка» преобразуется транслятором в кучу команд.

no-such-file ★★★★★ ()
Ответ на: комментарий от no-such-file

Каждая «строка» преобразуется транслятором в кучу команд.

А если строка — это просто строка, например «mama», как данные, он тоже разобьет ее на кучу команд? зачем?

phill ()
Ответ на: комментарий от no-such-file

Никак, процессор не знает ни о каких функциях и строках, он просто исполняет команды.

Ну правильно, Вы же сами себе противоречите. Транслятор запишет функцию в память, на эту область есть свой «индивидуальный» указатель, почему тогда процессор должен дробить эту область на куски, и исполнять по отдельности? Ему что, указатель не указ?

phill ()
Ответ на: комментарий от no-such-file

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

phill ()
Ответ на: комментарий от phill

на эту область есть свой «индивидуальный» указатель

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

почему тогда процессор должен дробить эту область на куски, и исполнять по отдельности?

Никто ничего не дробит. Эта область содержит команды процессора, составляющие функцию, процессор берет команду, исполняет, берет следующую, исполняет и т.д. Он не знает что это функция, ему всё равно, он просто исполняет команды одну за другой.

как данные, он тоже разобьёт её на кучу команд?

Данные не исполняются, они просто есть. Естественно, что процессор читает строку не всю сразу, а по частям (обычно равным длине слова процессора).

Процессор примитивен и оперирует только словами (кусками данных по N байт).

no-such-file ★★★★★ ()
Ответ на: комментарий от phill

А если строка — это просто строка, например «mama», как данные, он тоже разобьет ее на кучу команд? зачем?

а строка - это данные, зачем исполнять данные? :)

Harald ★★★★★ ()

можно.

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

выполнение одного и того же кода одновременно (если код не пытается модифицировать сам себя) - не проблема

MyTrooName ★★★★★ ()
Ответ на: комментарий от Harald

защита от дизассемблирования, сжатие кода, перепрошивка устройств

anonymous ()
Ответ на: комментарий от no-such-file

Никто ничего не дробит. Эта область содержит команды процессора, составляющие функцию, процессор берет команду, исполняет, берет следующую, исполняет и т.д. Он не знает что это функция, ему всё равно, он просто исполняет команды одну за другой.

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

Данные не исполняются, они просто есть

Но ведь программы — это и есть данные, мы же пока на фон-неймановской архитектуре. Для процессора нет разницы, когда он считывает область памяти, строка это или программа.

phill ()
Ответ на: комментарий от anonymous

фуфло такая защита, а в остальных случаях это уже код, помещённый в секцию данных

Harald ★★★★★ ()
Ответ на: комментарий от Harald

а строка - это данные, зачем исполнять данные? :)

Ну так любая программа для процессора — это данные.

phill ()
Ответ на: комментарий от phill

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

указатель не содержит информации о размере области, только адрес начала

и считывается не «вся область», а линейка кэша для кода, 32, 64 байт или сколько сейчас там, не знаю

Harald ★★★★★ ()
Ответ на: комментарий от phill

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

Harald ★★★★★ ()
Ответ на: комментарий от phill

Для процессора нет разницы, когда он считывает область памяти, строка это или программа.

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

Harald ★★★★★ ()

Пиши лучше scheme и/или racket в теги.

naryl ★★★★★ ()
Ответ на: комментарий от naryl

Пиши лучше scheme и/или racket в теги.

Лучше «легкая наркомания».

korvin_ ★★★★★ ()
Ответ на: комментарий от phill

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

anonymous ()
Ответ на: комментарий от phill

Если в двухпортовую память по одному адресу пишут в оба порта, то результат будет UB. Возможна порча данных.

anonymous ()
Ответ на: комментарий от no-such-file

Ну а прикинь, что у тебя нет кеша, нет шины с арбитром, и есть быстрая многопортовая SRAM, работающая на той же частоте, что и процессорные ядра. Тогда доступ будет реально одновременным. Например, на fpga так и выходит.

anonymous ()
Ответ на: комментарий от phill

Какую область, школоло упоротое?!? Ты про cache line так смешно рассуждаешь, да?

И нет давно никакого фон Неймана, кеши данных и инструкций разделены.

Есть нюансы, конечно же, типа embedded констант на ARM, но это уже точно не твоего упоротого ума дело.

anonymous ()
Ответ на: комментарий от phill

Если ты хочешь, чтобы строка была «просто строкой» - то тебе надо писать на ассемблере. Вот в ассемблере будет как ты хочешь.

Даже простая переменная может быть совсем не тем, что ты о ней думаешь. Она может не находиться на процессоре, а жить своей жизнью. Например, в лиспе под названием Clojure используется так называемая Software Transaction Memory (http://ru.wikipedia.org/wiki/Программная_транзакционная_память).

А изначальный твой пример был про банкинг. В банкинге тем более люди хранят не в «переменных», а в базах данных. БД медленная и сложная. Относительно сущностей в БД твои операции могут быть одновременными, и одновременно влиять на один и тот же кусок данных. Например, один тред у тебя читает первую строку таблицы, а другой вставляет в самый конец (если планировщик БД может доказать, что запрошенные операции не пересекаются физически, то он может выполнить их одновременно. Н-р MySQL таблицу заблокирует и выполнит синхронно, а MariaDB уже таблицу блокировать не будет).

Как сделать «простую переменную на одном процессоре» в твоем лиспе я понятия не имею, а вот в Java было бы достаточно добавить volatile.

Но учти, что если ты всё на свете сделаешь синхронным, тормозить оно будет пипец как!

stevejobs ★★★★☆ ()
Ответ на: комментарий от phill

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

stevejobs ★★★★☆ ()
Ответ на: комментарий от phill

Вот поэтому если ты пытаешься обычную переменную читать из разных тредов, может ВНЕЗАПНО оказаться, что из разных тредов эта переменная одновременно имеет разные значения.

stevejobs ★★★★☆ ()

Почитай про протоколы когерентности кешей (начни с MSI - он простой).
В частности, посмотри статьи Черемина (там, правда, Java, но не суть) - http://cheremin.blogspot.ru
начни с http://cheremin.blogspot.ru/2012/01/cache-coherency-basics-msi.html

kovrik ★★★★★ ()
Последнее исправление: kovrik (всего исправлений: 1)
Ответ на: комментарий от anonymous

Тогда доступ будет реально одновременным

Кто спорит?

no-such-file ★★★★★ ()

Рекомендую автору треда временно прекратить читать SICP и изучить любой вводный курс в CS, возможно, даже с небольшим заходом в EE.

Из развлекательных вариантов могу предложить курс http://www.nand2tetris.org/ .

anonymous ()

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

Puzan ★★★★ ()
Ответ на: комментарий от Puzan

Там все черз жопув силу чрезвычайного убожества технологии. Изучать надо современное открытое risc-ядро. OpenRISC или LEON, например.

anonymous ()
Ответ на: комментарий от anonymous

Ничего не через жопу, всё просто понятно и прямолинейно. А OpenRISC и Leon слишком много букаф, не нужно советовать новичку это читать. Пусть начнет с арифметики, алгебру потом сам освоит.

С 80-х годов (а точнее, с 60-х) ничего нового в устройстве вычислительной машины не придумали.

Кроме того, на 8080 очень подробная документация (понятная школьнику), в отличие от OpenRISC. Про Leon я вообще промолчу.

Puzan ★★★★ ()
Последнее исправление: Puzan (всего исправлений: 1)
Ответ на: комментарий от Puzan

ничего нового в устройстве вычислительной машины не придумали.

Ага, совсем ничего. Напрочь убили динамику, отошли от фон-Неймоновской модели дальше некуда, наплодили сущностей, слили программирование в сортир. А в 60-х еще лисп-машины работали, помнится. Такого не бывает, чтоб хомяк ничего не придумал. Еще как придумал. Усовершенствовал.

anonymous ()
Ответ на: комментарий от Puzan

8080

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

Алан Кей

Ъ:

архитектура была плохой

anonymous ()
Ответ на: комментарий от Puzan

Ага, просто-понятно, инструкции с неявными сайд эффектами, неортогональная ISA. Пусть тогда уж минимальный MIPS смотрит (Tiger).

anonymous ()
Ответ на: комментарий от anonymous

архитектура была плохой

Совершенно пофиг, что она была плохой для лиспа. Свои задачи она прекрасно решала (а её наследники до сих пор решают).

И вообще, пофиг, какая она была, главное она простая и понятная, что собственно и требуется.

Puzan ★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.