Как не забыть порядок параметров

Обсуждаются как существующие проекты (перевод документации, информационная система и т.п.), так и создание новых.

Модератор: Модераторы

Re: Как не забыть порядок параметров

Сообщение Mirage » 13.04.2014 12:21:39

скалогрыз писал(а):неудобно, наверное, работать с объектом, в котором ничего нельзя поменять?


Как обычно, есть как плюсы, так и минусы. В зависимости от ситуации.

скалогрыз писал(а):Соглашусь - Игры и GUI приложения случаи крайне редкие.


Случаи, когда в рамках создания GUI приложений нужно создавать много объектов в секунду крайне редкие. И с GUI'евостью никак это не связано.
С играми сложнее. В больших играх такое может понадобиться.

скалогрыз писал(а):Доказательство, того, что приведённый мною ранее код неправильный и работать не будет.


этот?
Код: Выделить всё
function BuildStampWithSomeData(const Title, Data: string; Price: double): TStamp;
begin
  Result:=TStamp.Create;
  Result.Title:=Title;
  ...
end;


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

скалогрыз писал(а):Облегчу твою задачу, Мираж. Программу я написал (прикладываю к сообщению)
Смело проверяй! ищи ошибки, с точки зрения синхронизации.


Доказывать корректность Ваших программ, оставляю Вам. Мы говорили о приведенных в топике отрывках.
А в программе, уверен, Вы учли то, о чем мы здесь говорим.

скалогрыз писал(а):Со своим уставом в чужой монастырь? ;) Ладно.


Не понял причем тут это.

скалогрыз писал(а):
Java Specs 17.5 писал(а):The usage model for final fields is a simple one: Set the final fields for an object in that object's constructor; and do not write a reference to the object being constructed in a place where another thread can see it before the object's constructor is finished. If this is followed, then when the object is seen by another thread, that thread will always see the correctly constructed version of that object's final fields.


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


Вы забыли упомянуть, что заботиться о синхронизации нужно в пределах конструктора. Вы часто раздаете ссылки на объект из конструктора, т.е. до завершения конструктора?
После завершения конструктора ссылки можно передавать без какой-либо синхронизации.

скалогрыз писал(а):Вот так. Нужно написать ImmutablePosition и для него билдер (соответственно) ;)


Зачем же так передергивать? Или Вы все же не понимаете концепции immutable объектов.
Если у вас есть в объекте position, который надо менять, то никакой это уже не immutable объект. Не дергаемся и делаем все mutable.
Если нужен immutable, то и position должен быть immutable.
Не говоря уже о том, что билдер для такого простого объекта как position в любом случае не понадобится.

скалогрыз писал(а):Для полноценной реализации Immutable-объектов, ему понадобится Immutable пара (для поточного использования)- ImmutablePosition.


Ввести immutable копии мутабельных объектов для многопоточного использования может быть неплохой идеей.
Билдеры для этого не понадобятся, т.к. immutable копии можно создавать, передавая в конструктор mutable оригинал.

скалогрыз писал(а):Ладно. Читаем Java устав дальше:
Java Specs: 17.5.3. Subsequent Modification of final Fields писал(а): Final fields can be changed via reflection and other implementation-dependent means.

О.о. коту под хвост. Хитрые программист начнут втихушечку переписывать значение "ручками" через reflection :) Компилятор же за ними теперь не уследит!


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

скалогрыз писал(а):Как следствие, приведённый в уставе пример с "Aggressive Optimization of final Fields", конечно, из области извращений.
Но ведь кто знает, что там люди могут понаписать, а как итог
Java Specs: 17.5.3. Subsequent Modification of final Fields писал(а):..., the compiler is allowed to reorder the reads of x and the call to g freely. Thus, new A().f() could return -1, 0, or 1.

Один и тот же код компилируется по разному, в зависимости от настроения комплиятора.


Этот пример тоже про использование reflection.
Да, кривыми руками можно все испортить.
Но это не повод отказываться от immutable классов, или цикла FOR.

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


Предсказуемо код работает в обоих случаях.
Просто для ручной синхронизации надо знать много неочевидных вещей. И писать немало кода этой самой явной синхронизации.
Кстати, немного ниже Вы против того, чтобы писать много кода.
Final поля штука полезная, но сложная в реализации.
А вот отсутствие volatile оправдать сложнее. В Java оно конечно тоже есть. Эмулировать его вручную то еще удовольствие.

скалогрыз писал(а):Ну конечно я не понимаю.
Мне говорят, что писать мало кода - это плохо, нужно писать много (и муторно) дополнительного кода, а в чём улучшение не говорят, но ссылаются на статью на модном сайте, перевода модного автора.
Улучшение-то в чём? Безопаснее? вовсе нет.


Про то, что мало кода - плохо, никто не говорил.
Где много кода Вы пытаетесь выдумать, но плохо получается. Есть только код билдера, который не всегда нужен.
Корректную альтернативу этому "много кода" Вы не предоставили.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Как не забыть порядок параметров

Сообщение скалогрыз » 13.04.2014 20:54:39

Mirage писал(а):
скалогрыз писал(а):Доказательство, того, что приведённый мною ранее код неправильный и работать не будет.


этот?
Код: Выделить всё
function BuildStampWithSomeData(const Title, Data: string; Price: double): TStamp;
begin
  Result:=TStamp.Create;
  Result.Title:=Title;
  ...
end;


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

Да, конечно! Особенно, если тело функции не содержит ошибок. По другому паскаль не работает.
И при этом совершенно не важно какой поток вызвлал эту функцию.
И именно по-этому функция является атомарной операцией создания и инициализации объекта. Этакий "внешний" конструтор или "лёгкий" билдер, если хочешь. "Лёгкий" - потому что не требует дополнительного описания класса и может быть описан вне декларации класса.
(На заметку про формулировки и термины: поля гарантированно инициализируются на момент создания объекта TStamp.Create, а вот заполнение данных происходит позже Result.Title:=Title. Но я понимаю, что под "инициализацией" мы имеем в виду именно заполнение данных в объекте.)

Mirage писал(а):Доказывать корректность Ваших программ, оставляю Вам. Мы говорили о приведенных в топике отрывках.
А в программе, уверен, Вы учли то, о чем мы здесь говорим.

Программа написана с учётом, того, что я говорил с самого начала. Что собственно и потврегается встречной критике, с твоей стороны.
Я любитель посмотреть в сущесвтующий код. (Talk is cheap. Show me the code. (с) Linus Torvalds)

Mirage писал(а):Вы забыли упомянуть, что заботиться о синхронизации нужно в пределах конструктора. Вы часто раздаете ссылки на объект из конструктора, т.е. до завершения конструктора?
После завершения конструктора ссылки можно передавать без какой-либо синхронизации.

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

С точки зрения Final полей - объект будет в актуальном состоянии после завершения конструтора, т.к. каждому из них должно быть присвоено значение (либо будет присвоено значение по-умолчанию).
С точки зрения функции (которую обсуждали выше) - объект будет в актульном состоянии по её завершению.

Mirage писал(а):Зачем же так передергивать? Или Вы все же не понимаете концепции immutable объектов.
Если у вас есть в объекте position, который надо менять, то никакой это уже не immutable объект. Не дергаемся и делаем все mutable.
Если нужен immutable, то и position должен быть immutable.
Не говоря уже о том, что билдер для такого простого объекта как position в любом случае не понадобится.

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

Потоко безопасность-то пропадает!
А что если программист Вася случайно или специально obj.pos.x переприсвоил. Хотя компилятор ему obj.x - не даст.

Код: Выделить всё
TPosition = class
  x,y, z: integer;
end;
TImmutableLabel = class
private
  x: integer;
  pos : TPosition;
public
  property pos : TPosition  read fpos; // ссылку pos поменять нельзя, а вот поля x,y,z  можно
  property x: Integer read fx;
end;

var
  l : TimmutableLabel
..
  l.pos.x:=10; // компилятор разрешит
  l.x:=10; // компилятор заругается


И совсем не нужно привязываться к самому классу TPosition слишком простой - это же пример. Такой класс может быть и сложнее. так ведь.

Mirage писал(а):Ну, таким способом можно и гарантии, скажем, цикла FOR отправить коту под хвост.
Reflection используют редко, и обычно зная, что делают.

Если лазейка есть, то ею будут пользоваться.

Mirage писал(а):
Java Specs: 17.5.3. Subsequent Modification of final Fields писал(а):..., the compiler is allowed to reorder the reads of x and the call to g freely. Thus, new A().f() could return -1, 0, or 1.


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

Нет. В уставе вообще нет ни одного примера с reflection. Reflection только упоминается (как штука для обхода final полей) .
Это пример про out-of-order execution, т.к. место присвоения final полей, просиходит на усмотрение компилятора.

С моей точки зрения, это очень странное дозволение компилятору.
Если я тебе скажу - поменяй две строчки инициалиации местами и потоки у тебя заработают - ты же на меня как на придурка посмотришь?!
А компилятор меняет строчки местами за тебя, не говорит тебе ничего, ты ему слепо веришь. А результат может противоречить ожиданиями.


Mirage писал(а):Просто для ручной синхронизации надо знать много неочевидных вещей. И писать немало кода этой самой явной синхронизации.
Кстати, немного ниже Вы против того, чтобы писать много кода.

Очень верно подмечено. Но в примере программы, всего 2 строчке синхронизации.
Код: Выделить всё
  t.Start;
...
  t.WaitFor;

Всё! больше нет. За каждой из этих обёрток скрывается (грубо говоря) по одному API вызову (StartThread и WaitForSingleObject - в случаях винды. И аналоги для PThread в unix-ах)
И больше нет ничего, ни мутексов, ни эвентов ни семафоров. Потому что, эта конкретная задача позволяет обойтись вот таким скромными минимумом.

Mirage писал(а):Final поля штука полезная, но сложная в реализации.
А вот отсутствие volatile оправдать сложнее. В Java оно конечно тоже есть. Эмулировать его вручную то еще удовольствие.

зачем писать сложно, если писать просто?
ни Final ни volatile не реализовать "вручную", потому что это всё инструкции компилятора - чтобы компилятор городил оптимизированный код.
Конечный программист тут безвластен соврешенно.
Но плюс в том, что из-за отсутствия оптимизации код будет выполнятся как есть, что даёт предсказуемость.

Mirage писал(а):Про то, что мало кода - плохо, никто не говорил.
Где много кода Вы пытаетесь выдумать, но плохо получается. Есть только код билдера, который не всегда нужен.
Корректную альтернативу этому "много кода" Вы не предоставили.

Ну как не представил-то. Представил - всё там, в том самом проекте.
1) нету immutable объекта;
2) нету билдера; (вместо него функция, но её назначение шире, чем билдер, т.к. порождает она изменяемый объект. Но и функцию от туда можно выкинуть, т.к. она там лишь для примера собственной атомарности.)
и всё работает.
Опять же, рекомендую - взять этот самый проект, переписать класс TStamp на builder pattern. Выкинуть Build- функцию, и сравнить итог.
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Как не забыть порядок параметров

Сообщение Mirage » 14.04.2014 22:28:06

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


Ага. Но ведь для обеспечения такой гарантии, должен быть специальный механизм. Сама она не возникнет. Это понятно?
Т.е. где-то должно быть задокументировано, что функция, создающая объект, не заинлайнится, поле создаваемого объекта не закэшируется в регистре процессора и т.п. Так? Тогда хотелось бы получить ссылку на то, где такое задокументировано.
Это из того, что язык может прогарантировать.

скалогрыз писал(а):А что означает "в пределах конструтора". В теле конструктора? не думаю.


Там прямым текстом написано:
Java spec писал(а): do not write a reference to the object being constructed in a place where another thread can see it before the object's constructor is finished


Object being constructed означает "объект, находящийся в процессе конструирования".
Вообще, фраза предельно четкая и ёмкая.

скалогрыз писал(а):А конструтор сам не занимается раздачей ссылок, так как это совсем не его задача.
Честно говоря, не понимаю, причём тут конструтор конкретно.
Ведь вопрос не в том конструктор или не конструток, а вопрос в том объект в актуальном состоянии или нет (пытаюсь не использовать термин "инициализирован").


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

скалогрыз писал(а):С точки зрения Final полей - объект будет в актуальном состоянии после завершения конструтора, т.к. каждому из них должно быть присвоено значение (либо будет присвоено значение по-умолчанию).


Да, и это прямо гарантировано в спецификации.

скалогрыз писал(а):С точки зрения функции (которую обсуждали выше) - объект будет в актульном состоянии по её завершению.


Где и как гарантируется это?

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


Какую обертку? Просто Position должен быть тоже immutable. Без всяких оберток.
И никакой Вася ничего не переприсвоит.
В противном случае, весь класс перестает быть immutable.
А если у Вас есть потребность в мутабельном варианте Position, то это дела не меняет - это трудности конкретного архитектурного решения.

скалогрыз писал(а):Если лазейка есть, то ею будут пользоваться.


Т.е. цикл FOR - отстой, ибо Васи регулярно перезаписывают счетчик прямой записью в память?

скалогрыз писал(а):
Mirage писал(а):
Java Specs: 17.5.3. Subsequent Modification of final Fields писал(а):..., the compiler is allowed to reorder the reads of x and the call to g freely. Thus, new A().f() could return -1, 0, or 1.


Этот пример тоже про использование reflection.
Да, кривыми руками можно все испортить.

Нет. В уставе вообще нет ни одного примера с reflection. Reflection только упоминается (как штука для обхода final полей) .
Это пример про out-of-order execution, т.к. место присвоения final полей, просиходит на усмотрение компилятора.


Как это нет? Опять недопонимаете прочитанное.
пример из спеки писал(а):
Код: Выделить всё
static void g(A a) {
        // uses reflection to change a.x to 2
    }

Комментарий ни о чем не говорит? Без "reflection and other implementation-dependent means" new A().f() вернет всегда строго 0, т.к. final поле изменить не получится.

скалогрыз писал(а):С моей точки зрения, это очень странное дозволение компилятору.
Если я тебе скажу - поменяй две строчки инициалиации местами и потоки у тебя заработают - ты же на меня как на придурка посмотришь?!
А компилятор меняет строчки местами за тебя, не говорит тебе ничего, ты ему слепо веришь. А результат может противоречить ожиданиями.


Ничего странного. Просто final поле это фактически константа. Вернее одноразово присваиваемое поле. И компилятор на полную катушку это использует для оптимизации.
Если программист все же меняет посредством хаков эти значения, то он берет всю ответственность на себя.

скалогрыз писал(а):Очень верно подмечено. Но в примере программы, всего 2 строчке синхронизации.
Код: Выделить всё
  t.Start;
...
  t.WaitFor;

Всё! больше нет. За каждой из этих обёрток скрывается (грубо говоря) по одному API вызову (StartThread и WaitForSingleObject - в случаях винды. И аналоги для PThread в unix-ах)
И больше нет ничего, ни мутексов, ни эвентов ни семафоров.


А сколько всего скрывается за этими API вызовами, лучше и не знать. Ибо там есть и синхронизация и все на свете. Уж лучше бы там были мьютексы или семафоры.

скалогрыз писал(а):Потому что, эта конкретная задача позволяет обойтись вот таким скромными минимумом.


Да, потому что задача вырождена с точки зрения многопоточности.
Каждый раз для обработки объекта создавать поток, да еще не один это невиданная роскошь.
Эдакая сверхтяжелая синхронизация.
Гораздо актуальнее ситуация, когда у нас крутятся несколько потоков, а мы подкладываем им тысячи объектов на обработку.
Да и говорили мы о том, что immutable объекты хороши тем, что позволяют с ними работать в разных потоках без синхронизации.

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


Volatile как раз об этом.
Не предсказуемость, а определенность. А определенность далеко не всегда нужна. Она нужна только там, где от неё зависит корректность.
А производительность тоже бывает нужна. Поэтому повсеместно отказываться от производительности в угоду определенности неприемлемо.

скалогрыз писал(а):Ну как не представил-то. Представил - всё там, в том самом проекте.
1) нету immutable объекта;
2) нету билдера; (вместо него функция, но её назначение шире, чем билдер, т.к. порождает она изменяемый объект. Но и функцию от туда можно выкинуть, т.к. она там лишь для примера собственной атомарности.)
и всё работает.


Все гораздо хуже:
1) Объект не immutable, но используется как будто бы он immutable. Что хрупко и ненадежно.
2) Билдер для такого простого объекта и так не нужен.
Работает лишь потому, что от краха спасает мега тяжелая синхронизация в виде создания и запуска потока.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Как не забыть порядок параметров

Сообщение скалогрыз » 15.04.2014 07:18:41

Mirage писал(а):Ага. Но ведь для обеспечения такой гарантии, должен быть специальный механизм. Сама она не возникнет. Это понятно?
Т.е. где-то должно быть задокументировано, что функция, создающая объект, не заинлайнится, поле создаваемого объекта не закэшируется в регистре процессора и т.п. Так? Тогда хотелось бы получить ссылку на то, где такое задокументировано.
Это из того, что язык может прогарантировать..

структурное программирование
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Как не забыть порядок параметров

Сообщение stanilar » 15.04.2014 14:00:01

Кажется я начинаю понимать. В обычном языке программирования, для использования потоков, программист пишет дополнительный вспомогательный код, и следит чтоб код в потоке лишний раз не обращался к обедоступным ресурсам. В java все наоборот, чтоб продемонстрировать всем скорость работы со строками, программист ВЕСЬ код пишат так, будто он в любой момент может огрести блокировку или неоднозначность данных.
stanilar
постоялец
 
Сообщения: 289
Зарегистрирован: 09.03.2010 19:09:02

Re: Как не забыть порядок параметров

Сообщение Mirage » 15.04.2014 18:51:59

скалогрыз писал(а):структурное программирование


Структурное программирование дает гарантии относительно видимости изменений между потоками?
Очень интересно.
Где про это почитать можно в общем и/или про конкретную реализацию в FPC/Delphi?

stanilar писал(а):Кажется я начинаю понимать. В обычном языке программирования, для использования потоков, программист пишет дополнительный вспомогательный код, и следит чтоб код в потоке лишний раз не обращался к обедоступным ресурсам. В java все наоборот, чтоб продемонстрировать всем скорость работы со строками, программист ВЕСЬ код пишат так, будто он в любой момент может огрести блокировку или неоднозначность данных.


Вы не правильно начинаете понимать. Блокировки и неоднозначность данных существуют безотносительно к языку программирования.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Как не забыть порядок параметров

Сообщение скалогрыз » 16.04.2014 08:04:14

Mirage писал(а):
скалогрыз писал(а):структурное программирование


Структурное программирование дает гарантии относительно видимости изменений между потоками?

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

Собственно тоже самое что требует от тебя устав явы - "не передавать указатель на объект, пока конструктор не завершён".

вот тебе пример, с "постоянными" потоками (создаются при запуске программы один раз, ну и освобождаются по завершению)
У вас нет необходимых прав для просмотра вложений в этом сообщении.
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Как не забыть порядок параметров

Сообщение Mirage » 16.04.2014 19:48:16

скалогрыз писал(а):ну конечно не даёт. Но оно даёт тебе гарантирю о порядке исполнения кода.


Т.е. структурное программирование запрещает процессору перетасовывать инструкции? А он об этом знает?

скалогрыз писал(а):Ты сам контролируешь время и место, когда данные в одном потоке становятся доступным в другом потоке.


Как раз без чего-то вроде volatile переменных, и соответствующей модели памяти, сложно контролировать видимость данных. Приходится использовать тяжелые синхронизации там, где можно было бы обойтись просто записью в память.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Как не забыть порядок параметров

Сообщение скалогрыз » 16.04.2014 20:14:08

Mirage писал(а):Т.е. структурное программирование запрещает процессору перетасовывать инструкции? А он об этом знает?

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

Mirage писал(а):Как раз без чего-то вроде volatile переменных, и соответствующей модели памяти, сложно контролировать видимость данных. Приходится использовать тяжелые синхронизации там, где можно было бы обойтись просто записью в память.


Википедия
Volatile_variable писал(а):Operations on volatile variables are not atomic, nor do they establish a proper happens-before relationship for threading. This is according to the relevant standards (C, C++, POSIX, WIN32),[2] and this is the matter of fact for the vast majority of current implementations. Thus, the usage of volatile keyword as a portable synchronization mechanism is discouraged by many C/C++ groups

И внезапно:
Volatile_variable писал(а):(In Java 5 or later) Volatile reads and writes establish a happens-before relationship, much like acquiring and releasing a mutex.

Что заставляет меня думать, что каждая такая переменная обёрнута в мьютекс (скрытый от программиста). Скрытые "тяжёлые" синхронизации.
И тут возникает вопрос, а мьютекс на каждую volatile переменную - что очень расходно, или один мьютекс на все volatile - что приводит к тормозам?

И про запись в память:
Код: Выделить всё
class FinalFieldExample {
    final int x;
    int y;
    static FinalFieldExample f;

    public FinalFieldExample() {
        x = 3;
        y = 4;
    }

    static void writer() {
        f = new FinalFieldExample();
    }

    static void reader() {
        if (f != null) {
            int i = f.x;  // guaranteed to see 3 
            int j = f.y;  // could see 0
        }
    }
}

Не совсем понятно почему "// could see 0"
Это может быть так, но только если порядок исполнения инструкций следующий:
Код: Выделить всё
temp <- allocate memory for FinalFieldExample;
temp.x <- 3;  // x - финальное поле;
f <- temp; // пере присвоили ссылку- "создали видимость" объекта, ещё до завершения конструктора?
temp.y <- 4;  // y - не финальное поле;


тогда сама проблема, может опять же изличится , следующим образом:
Код: Выделить всё
   static void writer() {
        FinalFieldExample r = new FinalFieldExample();
        f = r;
    }

хотя, тут, конечно, вылезет оптимизатор (или может вылезти), и вырежет использование "r", что вернёт нас к проблеме.
Но, с точки зрения структурного программирования, конструктор должен быть завершён, ещё до присвавания f = r; и потому все переменные будут инициализированы, ещё до того, как их увидят другие потоки (значение f будет null на момент исполнения r = new FinalFieldExample() ).
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Как не забыть порядок параметров

Сообщение Mirage » 17.04.2014 20:41:50

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


Для процессора не существует никаких подпрограмм.

скалогрыз писал(а):Я как-то сомневаюсь, что процессор cначала выйдет из подпрограммы, а потом будет какой-то код из неё довыполнять.
Это уже не тосование получается, а модификация кода и памяти.


Как-то сомневаюсь, что программы, корректность которых основывается на "как-то сомневаюсь" будут хорошо работать.
Да и компилятору выкидывать переменные из регистров на том основании, что подпрограмма закончилась, я бы тоже не советовал. Тогда еще и медленно будут работать.

скалогрыз писал(а):Что заставляет меня думать, что каждая такая переменная обёрнута в мьютекс (скрытый от программиста). Скрытые "тяжёлые" синхронизации.
И тут возникает вопрос, а мьютекс на каждую volatile переменную - что очень расходно, или один мьютекс на все volatile - что приводит к тормозам?


Фантазировать не стоит. Мьютексов там нет, есть пара барьеров, реализуемых на x86 инструкциями xchg и add с префиксом lock. И те при записи только.
Можно и еще эффективнее, используя специальные инструкции, типа mfence, но это как-то конфликтует с JMM. В недавно вышедшей Java 8 вроде как раз это победили.
А в C++ с этим бардак еще тот. В VS2005, например, volatile ведут себя похоже на явовские. Модели памяти тогда не было единой.
Возможно, появившаяся в C++11 модель памяти это стандартизирует.

скалогрыз писал(а):И про запись в память:
Не совсем понятно почему "// could see 0"
Это может быть так, но только если порядок исполнения инструкций следующий:
Код: Выделить всё
temp <- allocate memory for FinalFieldExample;
temp.x <- 3;  // x - финальное поле;
f <- temp; // пере присвоили ссылку- "создали видимость" объекта, ещё до завершения конструктора?
temp.y <- 4;  // y - не финальное поле;


Ну да, он может быть и таким.

скалогрыз писал(а):тогда сама проблема, может опять же изличится , следующим образом:
Код: Выделить всё
   static void writer() {
        FinalFieldExample r = new FinalFieldExample();
        f = r;
    }

хотя, тут, конечно, вылезет оптимизатор (или может вылезти), и вырежет использование "r", что вернёт нас к проблеме.
Но, с точки зрения структурного программирования, конструктор должен быть завершён, ещё до присвавания f = r; и потому все переменные будут инициализированы, ещё до того, как их увидят другие потоки (значение f будет null на момент исполнения r = new FinalFieldExample() ).


"лечение" не поможет, ибо лишняя запись во-первых соптимизируется, во-вторых может передвинуться точно также.
И структурное программирование тут не причем. Оно гарантирует что-то только в рамках одного потока. Все эти оптимизации и перемещения инструкций как компилятором, так и процессором производятся так, что не могут быть обнаружены в рамках одного потока.
Но другие потоки их видят и надо иметь возможность малой кровью влиять на то, что они видят.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Как не забыть порядок параметров

Сообщение скалогрыз » 17.04.2014 21:16:35

Немножко странно говорить о процессоре и Ява одновременно =) Ява это ж байт код!

Mirage писал(а):Для процессора не существует никаких подпрограмм.

Но ты прав насчёт процессора, этих команд в нём не существует:
(x86) RET
(ARM) RET
CALL это тоже ассемблерная выдумка.

Mirage писал(а):Как-то сомневаюсь, что программы, корректность которых основывается на "как-то сомневаюсь" будут хорошо работать.
Да и компилятору выкидывать переменные из регистров на том основании, что подпрограмма закончилась, я бы тоже не советовал. Тогда еще и медленно будут работать.

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

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

Mirage писал(а):Фантазировать не стоит. Мьютексов там нет, есть пара барьеров, реализуемых на x86 инструкциями xchg и add с префиксом lock. И те при записи только. Можно и еще эффективнее, используя специальные инструкции, типа mfence, но это как-то конфликтует с JMM. В недавно вышедшей Java 8 вроде как раз это победили.

Ну и кто из нас фантазирует? Покажи лучше скомпилированный код.
Я тебе уже две программы предоставил, а ты пока ничего ;) только домыслы.

Mirage писал(а):А в C++ с этим бардак еще тот. В VS2005, например, volatile ведут себя похоже на явовские. Модели памяти тогда не было единой.
Возможно, появившаяся в C++11 модель памяти это стандартизирует.

Раз бардак непредсказуемый - используй объекты синхронизации.

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

Ну как не причём.
Если тебе даже устав говорит - "заверши конструктор раньше, чем ты его передашь ссылку на объект в другой поток".
Исключительное требование на последовательность операций (структурность): заверши А (конструтор), прежде, чем сделать Б (передать ссылку в другой потоки).

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

Теперь понятно зачем в яве билдер - гарантировать конструтор! Через лишний класс, чтобы оптимизатор его не выкинул.
И всё ещё не понятно зачем этот билдер в паскале.
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Как не забыть порядок параметров

Сообщение hinst » 17.04.2014 22:46:24

скалогрыз писал(а):Теперь понятно зачем в яве билдер - гарантировать конструтор! Через лишний класс, чтобы оптимизатор его не выкинул.


это вряд ли.......
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Как не забыть порядок параметров

Сообщение скалогрыз » 17.04.2014 23:23:33

hinst писал(а):
скалогрыз писал(а):Теперь понятно зачем в яве билдер - гарантировать конструтор! Через лишний класс, чтобы оптимизатор его не выкинул.


это вряд ли.......

:mrgreen: :mrgreen: :mrgreen:

ну конечно, билдер это просто удобный конструктор immutable объектов.

Кстате, hinst! тут пример был stamp2 - там используется объект, к которому прикручен внешний ref-counting для синхронизации потоков.
Так что посмотри, если интересно - ответственности за вызов AddRef, DecRef руками с тебя это не снимает :)
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Как не забыть порядок параметров

Сообщение Mirage » 18.04.2014 18:25:45

скалогрыз писал(а):Немножко странно говорить о процессоре и Ява одновременно =) Ява это ж байт код!


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

скалогрыз писал(а):Но ты прав насчёт процессора, этих команд в нём не существует:
(x86) RET
(ARM) RET
CALL это тоже ассемблерная выдумка.


Опять передергиваем?
В описании команд как-то не видно каких-либо упоминаний структурного программирования и гарантий, связанных с ним.

скалогрыз писал(а):Не надо точить программы под железо, это задача компилятора.
Если компилятор вредничает, то нужно его либо чинить, либо не использовать вообще.


Вот тут соглашусь.

скалогрыз писал(а):Ну и кто из нас фантазирует? Покажи лучше скомпилированный код.


Код чего? Как устроен volatile в яве явно не по коду надо смотреть.
Если интересно можно почитать:
https://blogs.oracle.com/dave/entry/ins ... ile_fences

скалогрыз писал(а):Я тебе уже две программы предоставил, а ты пока ничего ;) только домыслы.


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

скалогрыз писал(а):Раз бардак непредсказуемый - используй объекты синхронизации.


О том и говорю. Синхронизацию надо использовать, в т.ч. легкую, встроенную в конструкции языка, типа иммутабельных объектов и volatile переменных.
А в ответ услышал, что создание экземпляра через функцию что-то гарантирует и т.п. ерунду.
И, видимо, все эти конструкции выдумываются зря. Делать людям нечего.
Рад, что есть понимание.

скалогрыз писал(а):Если тебе даже устав говорит - "заверши конструктор раньше, чем ты его передашь ссылку на объект в другой поток".
Исключительное требование на последовательность операций (структурность): заверши А (конструтор), прежде, чем сделать Б (передать ссылку в другой потоки).


Это он как раз в контексте создания immutable экземпляра говорит.
Если создаваемый объект не immutable, то завершения A до делания Б ничего не даст.

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


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

скалогрыз писал(а):Теперь понятно зачем в яве билдер - гарантировать конструтор! Через лишний класс, чтобы оптимизатор его не выкинул.


Ну вот зачем писать явную чушь? Тем более что обсудили достаточно подробно зачем и в каких случаях нужен билдер.

скалогрыз писал(а):И всё ещё не понятно зачем этот билдер в паскале.


Кто-то говорил что билдер нужен в Паскале?
Вот модель памяти было бы здорово иметь. Тогда, возможно, и билдер бы пригодился.
Mirage
энтузиаст
 
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia

Re: Как не забыть порядок параметров

Сообщение скалогрыз » 18.04.2014 19:45:06

Mirage писал(а):
Mirage писал(а):Для процессора не существует никаких подпрограмм.

Опять передергиваем?
В описании команд как-то не видно каких-либо упоминаний структурного программирования и гарантий, связанных с ним.

я не говорил про струтурное программирование, я говорил про "отсутствие подпрограмм" ;) специально-же цитату посдставляю!
...и выражаю своё сомнение в твоей уверенности, относительно того как и что оптимизирует процессов.

Mirage писал(а):А в ответ услышал, что создание экземпляра через функцию что-то гарантирует и т.п. ерунду.

Не стоит алгоритмические проблемы пытатся решить средствами языка.

Очереднёсть инструкций гарантируется в паскале для конкретного потока.
Это значит, что создав объект, я точно уверен, что все пременные в нём как надо, и я могу спокойно его раздавать в другие потоки
(Что приведено в примере программок! я же использую не мутабельный объект).
По твоим утверждениями, такой гарантии (даже для одно потока) в Яве нет. ...но на самом деле есть, но это неважно.

Замечу, что пример приведённый в ява уставе использует глобальную переменную "static FinalFieldExample f;"... что как бы зло изначальное. Будь это не глобальной переменной, у тебя был бы хороший контроль над тем, когда другие потоки её увидят.

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

Mirage писал(а):О том и говорю. Синхронизацию надо использовать, в т.ч. легкую, встроенную в конструкции языка, типа иммутабельных объектов и volatile переменных. И, видимо, все эти конструкции выдумываются зря. Делать людям нечего.

Mirage писал(а):Распихивать все это вручную тоже радости мало.

Радости мало, зато очень точно и эффективно можно контроллировать что, куда и когда попадёт.
Контроль = Надёжность.

---
Итог: билдер патерн, нужен только в Яве, в паскале он не нужен! Ура! Ява программисты, пишите больше и толще! :mrgreen:
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Пред.След.

Вернуться в Разное

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 12

Рейтинг@Mail.ru