Многопоточность
От: C.A.B LinkedIn
Дата: 30.04.13 16:57
Оценка: 2 (1)
Я верю что за многопоточными(параллельными) программами бушующее (которое уже почти настоящее ) и потому изначально проектировал свой ЯП со "встроенным" параллельным программированием.
Этот пост я писал как ответ на сообщения AlexRK
Автор: AlexRK
Дата: 30.04.13
и kaa.python
Автор: kaa.python
Дата: 30.04.13
в ветке про дедлоки. Но решил вынести его в отдельный тред, так как хочу обсудить ниже описанную концепцию.

CAB>>Можно запретить закольцовывание ожиданий потоков.

KP>А как это можно сделать на этапе компиляции?

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

Внутри функции есть два вида синхронизации:
По результатам:
  ...
  let f = foo()   //Вызов мееедленой функции возвращающей Int
  ...             //Делаем что-то пока foo выполняется
  let s = f + 1   //Здесь останавливаемся и ждём пока foo вернёт результат(ели ещё не вернула)
  ..


И по выходу:
  let bar = {)    //Это функция bar
  ...
  foo()           //Вызов мееедленой функции
  ...
  (}              //Не выходим пока foo не завершится


Для случая когда все функции чистые проблема дедлоков решается "сама собой", так как вызовы представляют из себя дерево без перекрёстных зависимостей, например:
  doer d = {) foo(); stop() (}          //main
  def foo = {) stop(); bar() (}
  def bar = {) stop(); stop() (}
  def stop = {) while true => nothing (}

Когда программа остановится дерево вызовов будет иметь такой вид:


Такой синхронизации достаточно для реализации некоторых простых вещей, например для параллельной обработки данных:
   def procData = {l:LIST[String])   //Функция подсчитывает количество символов в списке строк.
     let s = LIST[Int]               //Список int'ов 
     foreach i in l => s :: size(i)  //Число символов подсчитывается для каждой строки, затем результат помещается список int'ов(без сохранения порядка).
     let r = 0                       //Для суммирования
     foreach i in s => r := r + i    //Сложение всех int'ов*
     r
   (Int}

*Сразу подсчитать сумму("foreach i in l => r := r + size(i)") в рамках этой модели не получится, так как на второй итерации оператор + не сможет прочитать r пока не завершится вызов size() из первой, т.е. параллелизма не будет.

Когда появляются общие переменные всё становится несколько сложнее, например:
  doer d = {) foo(); bar() (}
  def foo = {) v1 := v1 + v2 (}
  def bat = {) v2 := v2 + v1 (}
  var v1 = 0
  vat v2 = 0

Этот код можно представить так:

И соответственно граф зависимостей будет выглядеть так:

Что определённо плохо, так как в более сложных случаях может привести к дедлокам. Чтобы этого избежать нужно сделать чтобы в любой момент времени существовала только одна из зависимостей(a или b). Для чего я хочу использовать хитрый алгоритм синхронизации для общих переменных:

У каждой общей переменной может быть только одна изменяющая её функция, т.е. один писатель много читателей(это ограничения для оптимизации).
Каждая функция-писатель является критической секцией и захватывает записываемые переменные на время своего выполнения, кроме того пред захватом она читает все требуемые общие переменные, т.е это примерно эквивалентно передаче их через аргументы, например для foo из пример выше:
  def foo = {v:Int) v1 := v1 + v (}

Чтение:
1.Захварываем общую переменную на чтение(если выполняется запись ждём).
2.Читаем.
3.Освобождаем.

Запись:
1.Читаем используемые переменные.
2.Захватываем переменную на запись(если выполняется чтение ждём завершения не допуская старта новых чтений).
3.Выполняем тело функции-писателя.
4.Освобождаем.

Таким образом, например, если первой начнёт выполнятся foo то она предварительно(перед началом выполнения тела) прочитает переменную v2, и во время выполнения тела зависимости "a" не будет, будет только "b", т.е. bar будет ожидать завершения foo.

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

Это будет работать? Как по вашему, сложно писать в таком стиле(когда каждый вызов это отдельный поток)?
Спасибо.
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re: Многопоточность
От: batu Украина  
Дата: 30.04.13 17:15
Оценка:
Здравствуйте, C.A.B, Вы писали:

CAB> Я точно не знаю, но вот как собираюсь сделать: следуя идее "автоматическое управление потоками"("У нас есть авт. управление памятью, почему мы до сих пор управляем потоками в ручную?"), я хочу "выкинуть" само понятие "поток"(как некая отдельная сущность), и сделать почти как в Go "все функции выполняются параллельно"(в Go не все), т.е. каждый вызов выполняется в новом потоке. В тоже время внутри функции как-бы(с точки зрения программиста) существует только один поток, т.е. если я например пишу чистую функцию(не обращающуюся к общим переменным), то о синхронизации в общем-то можно почти не думать.


Это и есть парадигма функционального программирования. Там нет "общих переменных" (наверное, правильно будет сказать глобальные переменные). Но вот последовательность есть. Правда есть еще и "ленивые вычисления". В общем, все это уже было.
Хорошая идея разделить участки там где идет только чтение, и там где допустимо изменение хорошая, но императивная парадигма основана на присваивании. Нужна другая парадигма. Например, машины управляемые потоками данных.. Ну, и еще кое-что есть.. в этом духе
Re: Многопоточность
От: AlexRK  
Дата: 30.04.13 20:01
Оценка:
Здравствуйте, C.A.B, Вы писали:

CAB> Я точно не знаю, но вот как собираюсь сделать: следуя идее "автоматическое управление потоками"("У нас есть авт. управление памятью, почему мы до сих пор управляем потоками в ручную?"), я хочу "выкинуть" само понятие "поток"(как некая отдельная сущность), и сделать почти как в Go "все функции выполняются параллельно"(в Go не все), т.е. каждый вызов выполняется в новом потоке. В тоже время внутри функции как-бы(с точки зрения программиста) существует только один поток, т.е. если я например пишу чистую функцию(не обращающуюся к общим переменным), то о синхронизации в общем-то можно почти не думать.


Если функции чистые, то получается зависимость по данным. Семантика программы при этом не зависит от того, есть вообще параллелизм или нет. Функции могут быть выполнены и последовательно, и параллельно — результат будет одинаков. Главное, что соблюдается требуемая последовательность вызовов.

Но — функции это не потоки. Потоки могут обмениваться сообщениями. У вас аналога такого варианта не предусмотрено?
А где есть примитивы send и receive, там могут быть и дедлоки. И разрулить такие конфликты на этапе компиляции сложно, если вообще возможно...

CAB> Когда появляются общие переменные всё становится несколько сложнее, например:


Думаю, такая синхронизация убьет всю достигнутую за счет параллелизма производительность.
Re: Многопоточность
От: Pzz Россия https://github.com/alexpevzner
Дата: 30.04.13 20:28
Оценка:
Здравствуйте, C.A.B, Вы писали:

CAB>Внутри функции есть два вида синхронизации:

CAB> По результатам:
CAB>
CAB>  ...
CAB>  let f = foo()   //Вызов мееедленой функции возвращающей Int
CAB>  ...             //Делаем что-то пока foo выполняется
CAB>  let s = f + 1   //Здесь останавливаемся и ждём пока foo вернёт результат(ели ещё не вернула)
CAB>  ..
CAB>


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

Работает это довольно средненько — именно поэтому индустрия пошла по пути многоядерных процессоров, с явно выделенными потоками исполнения, а не по пути процессоров с очень большой степенью параллелизмом на уровне команд.
Re: Многопоточность
От: Kubyshev Andrey  
Дата: 01.05.13 04:45
Оценка: +1
Здравствуйте, C.A.B, Вы писали:

CAB>Я верю что за многопоточными(параллельными) программами бушующее (которое уже почти настоящее ) и потому изначально проектировал свой ЯП со "встроенным" параллельным программированием.


А разве сейчас многопоточные программы это трудность? Чего ты хочешь этим достичь ? Скорости, масштабируемости ? Какие проблемы решит ?
Re[2]: Многопоточность
От: C.A.B LinkedIn
Дата: 01.05.13 06:03
Оценка:
Здравствуйте, AlexRK, Вы писали:
ARK>Но — функции это не потоки*. Потоки могут обмениваться сообщениями. У вас аналога такого варианта не предусмотрено?
*В данном контексте под "потоки" я подразумеваю параллельно выполняющейся код(потоки выполнения/инструкций), не зависимо от того обмениваются ли они сообщениями.
Пока нет, пока я не вижу особой нужды в них(как в прочих wait-конструкциях).
Типичный паттерн использования сообщений выглядит примерно так (кто пользуется сообщениями поправьте если я не прав):
  //Поток-отправитель
  ...
  while(...){
    ...
    trd.send("Some message")
    ...
  }
  ...
  //Поток-получаетль
  ...
  while(...){
    val m = waitMessage() //Ждём сообщение
    ...                   //Обрабатываем
  }
  ...

Если каждый вызов это отдельный поток, то можно сделать так:

  //Поток-отправитель
  ...
  while(...){
    ...
    massageProcessor("Some message")
    ...
  }
  ...
  //Поток-получаетль
  def massageProcessor(m:String) = {
    ...        //Обработка сообщения
  }

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

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

Почему вы так думаете? Есть алгоритмы для оптимизации всего этого (если вам интересно я распишу подробней).
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re: Многопоточность
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 01.05.13 06:24
Оценка: 10 (3)
Здравствуйте, C.A.B, Вы писали:

CAB>Это будет работать? Как по вашему, сложно писать в таком стиле(когда каждый вызов это отдельный поток)?


Простите, а зачем делать только такой стиль? Нужны и последовательные конструкции. Вы Occam видели? Судя по вопросам, нет. А для написания таких языков это даже не классика — это как текст из Библии для христианина. Обратите внимание, как легко в нём синтаксически организуется и параллельное, и последовательное выполнение, и связь между частями.

Ознакомьтесь, и по результатам можете вернуться к своему проектированию.
The God is real, unless declared integer.
Re[2]: Многопоточность
От: AlexRK  
Дата: 01.05.13 06:28
Оценка: +1
Здравствуйте, Kubyshev Andrey, Вы писали:

KA>Здравствуйте, C.A.B, Вы писали:


CAB>>Я верю что за многопоточными(параллельными) программами бушующее (которое уже почти настоящее ) и потому изначально проектировал свой ЯП со "встроенным" параллельным программированием.


KA>А разве сейчас многопоточные программы это трудность?


А разве нет?
Re[2]: Многопоточность
От: C.A.B LinkedIn
Дата: 01.05.13 06:40
Оценка:
Здравствуйте, Kubyshev Andrey, Вы писали:
KA>А разве сейчас многопоточные программы это трудность?
Сейчас, если ты например захочешь написать параллельное приложение(даже банальный GUI — work с парой потоков), тебе нужно будет думать о многих вещах: о запуске потоков, о корректном завершении потоков, о приостановке(wiat'ы), об синхронизации, об разделяемых ресурсах и т.п.
KA>Чего ты хочешь этим достичь ?
Я хочу чтобы программисты (в частности я ) тратили меньше времени и сил на написание параллельных приложений. В идеале полностью автоматизировать управление потоками(процессорным временем), как это было сделано с памятью.
KA>Скорости, масштабируемости ?
Список приоритетов выглядит примерно так: простота, надёжность, масштабируемость, скорость.
KA>Какие проблемы решит ?
Прежде всего проблему освоения вычислительной мощности современных процессоров (разробы процессоров вдохнут с облегчением ).
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re: Многопоточность
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 01.05.13 06:41
Оценка: 4 (1)
Здравствуйте, C.A.B, Вы писали:

CAB>Я верю что за многопоточными(параллельными) программами бушующее (которое уже почти настоящее ) и потому изначально проектировал свой ЯП со "встроенным" параллельным программированием.

CAB>Этот пост я писал как ответ на сообщения AlexRK
Автор: AlexRK
Дата: 30.04.13
и kaa.python
Автор: kaa.python
Дата: 30.04.13
в ветке про дедлоки. Но решил вынести его в отдельный тред, так как хочу обсудить ниже описанную концепцию.


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

А то, о чем ты говоришьо, уже реализовано в функциональных языках.

CAB>Это будет работать? Как по вашему, сложно писать в таком стиле(когда каждый вызов это отдельный поток)?


В этом стиле писать будет очень сложно, потому что задача усложняется во много раз. По сути параррелизм у нас может появляться в самых неожиданных местах. Синхронизация между потоками тема нетривиальная, и везде ее стараются свести к минимуму. Потому что вечно где-нить что-нить упустишь, и оно выстрелит в самый неподходящий момент но неизвестно когда. Ты же предлагаешь это делать в каждой функции
Re[2]: Многопоточность
От: C.A.B LinkedIn
Дата: 01.05.13 06:52
Оценка:
Здравствуйте, batu, Вы писали:
B>Хорошая идея разделить участки там где идет только чтение, и там где допустимо изменение хорошая, но императивная парадигма основана на присваивании. Нужна другая парадигма. Например, машины управляемые потоками данных.. Ну, и еще кое-что есть.. в этом духе
Потоки данных штука хорошая(хоть и сложная), в будущем я бы хотел добавить их так-же. Но пока "разбираюсь" с потоками инструкций
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re[3]: Многопоточность
От: AlexRK  
Дата: 01.05.13 06:56
Оценка: 4 (1)
Здравствуйте, C.A.B, Вы писали:

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


Понятно. Нечто вроде SCOOP в Eiffel. Но как быть, когда между потоками нет отношения "родитель-подчиненный"? К примеру, один поток — это диспетчер IO-вызовов, а другой — наша программа. Диспетчер мы создать не можем, он существовал в операционной системе до того, как мы появились на свет, и останется после того, как мы умрем. (И наоборот — наша программа не создается этим диспетчером, ее запускает пользователь.) А общаться с ним надо. Тут для вашей модели нужен какой-то аналог общих переменных на уровне ОС.

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

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

CAB>Почему вы так думаете? Есть алгоритмы для оптимизации всего этого (если вам интересно я распишу подробней).

Сомнительно все это выглядит. Формализовать ощущения пока не могу.

Кстати, вроде у вас дедлок все равно возможен. Псевдокод:
  shared int a;
  shared int b;

  void foobar()
  {
    foo();  // spawn потока, захват переменной "a", возврат управления
    bar();  // spawn потока, захват переменной "b", возврат управления - все успешно проходит
  }

  void foo()
  {
    use(a);

    ... // тут чего-то долго делаем

    bar();  // ВЫЗОВ НОМЕР 2: попытка захвата переменной "b", а вот и дедлок
  }

  void bar()
  {
    use(b);
  }


Или же у вас функция должна блокировать все общие переменные на всю глубину вызовов, которые изнутри ее делаются. Это, по-моему, просто невозможно, т.к. в конечном счете вся программа вызывается из одной функции.
Re[4]: Многопоточность
От: AlexRK  
Дата: 01.05.13 07:00
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK>Кстати, вроде у вас дедлок все равно возможен. Псевдокод:


Да, тут естественно bar должен тоже вызывать foo:

  void bar()
  {
    use(b);

    ... // тут чего-то долго делаем

    foo();  // дедлок
  }
Re[4]: Многопоточность
От: C.A.B LinkedIn
Дата: 01.05.13 09:45
Оценка:
ARK>Понятно. Нечто вроде SCOOP в Eiffel. Но как быть, когда между потоками нет отношения "родитель-подчиненный"? К примеру, один поток — это диспетчер IO-вызовов, а другой — наша программа. Диспетчер мы создать не можем, он существовал в операционной системе до того, как мы появились на свет, и останется после того, как мы умрем. (И наоборот — наша программа не создается этим диспетчером, ее запускает пользователь.) А общаться с ним надо. Тут для вашей модели нужен какой-то аналог общих переменных на уровне ОС.
Как вариант можно установить такие отношения, как например это делается в WinAPI: мы "говорим" системе "вот у нас есть callback функция(WinProc), дёргай её если что"
ARK>Кстати, как мне кажется, прием-отправка сообщений и синхронизация на общих переменных — дуальные сущности, можно одно реализовать поверх другого. Если дедлок возможен в одном месте, то и в другом тоже. Но сообщения мне больше нравятся, как-то они прозрачнее для понимания.
Можно так реализовать:
  //Очередь сообщений
  def q = LIST[Strin]
  //Поток-отправитель
  ...
  while(...){
    ...
    send("Some message")
    ...
  }
  ...
  def send = {m:String) //Функция-писатель
    q := q :: m
  (}
  //Поток-получаетль
  ...
  while(...){
    if isEmpty(q) => sleep(50)
    else => ... //Обработка   
  }
  ...

ARK>Кстати, вроде у вас дедлок все равно возможен. Псевдокод:
ARK>
ARK>  void foo()
ARK>  {            //!!Для читаемых перменных здесь(пред выполнением тела функции) выполняется чтение(копирование) значения,                
ARK>               //т.е. foo() не начнёт выполнение пока не прочтиает "a", и  затем сразу освободи его, так что новый вызов foo() так же сможет выполнить чтение.
ARK>               //Для записываемых переменных здесь выполнятся захват(с освобождением по завершении выполнения тела функции),  так что новый вызов foo() не начнётся пока не будет
ARK>               //выполнен предыдущий.
ARK>    use(a);
ARK>    ... // тут чего-то долго делаем
ARK>    bar();  // ВЫЗОВ НОМЕР 2: попытка захвата переменной "b", а вот и дедлок
ARK>  }
ARK>

Как-то так.
ARK>Или же у вас функция должна блокировать все общие переменные на всю глубину вызовов, которые изнутри ее делаются.
Нет, только в передела одной функции.
Синхронизация доступа к переменным вообще нужна чтобы не дать одному потоку "втихаря" подменить значение переменной пока другой поток с ней работает. Например в Scala меня делали грустным внезапные и не предсказуемые исключения (NullPointerException) в коде на подобии:
  ...
  if(n != null){   //n - разделяемая переменная
    n.m()}         //Здесь поток иногда вылетает по NullPointerException, потому-что другой поток обнулил n между проверкой и вызовов метода m()
  ...


PS:
Пока писал ответ придумал как подвесить это :
  var v1 = 0
  def w = {)    //Функция-писатель
    let s = r()
    v1 := 1 + s
  (}
  def r = {)   //Функция-читатель
    v1 
  (}

Т.е. w блокирует переменную v1 и вызывает r, затем ожидает завершения r. Стартуя r останавливается в ожидание завершения w. Дедлок.
Возможное решение — не блокировать v1 на время выполнения функции-писателя, и обновлять значение по её завершению.
В общем я ещё подумаю над этим.
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re[3]: Многопоточность
От: batu Украина  
Дата: 01.05.13 09:53
Оценка:
Здравствуйте, C.A.B, Вы писали:

CAB>Здравствуйте, batu, Вы писали:

B>>Хорошая идея разделить участки там где идет только чтение, и там где допустимо изменение хорошая, но императивная парадигма основана на присваивании. Нужна другая парадигма. Например, машины управляемые потоками данных.. Ну, и еще кое-что есть.. в этом духе
CAB>Потоки данных штука хорошая(хоть и сложная), в будущем я бы хотел добавить их так-же. Но пока "разбираюсь" с потоками инструкций
Ничего сложного. Пришли данные (или изменились)- это событие. Событие имеет значение. Вот формирование события не изменят данные, а анализирует. Т.е. оно может выполняться параллельно. Поток инструкций обрабатывающий это событие пользуется пространством конкретного объекта и значением события в качестве параметра. Так что вполне может выполняться параллельно синхронизируясь только по доступу к объекту. Если это в ООП.
Re: Многопоточность
От: Mamut Швеция http://dmitriid.com
Дата: 01.05.13 10:20
Оценка: 9 (2) +1
CAB>Внутри функции есть два вида синхронизации:
CAB> По результатам:
CAB>
CAB>  ...
CAB>  let f = foo()   //Вызов мееедленой функции возвращающей Int
CAB>  ...             //Делаем что-то пока foo выполняется
CAB>  let s = f + 1   //Здесь останавливаемся и ждём пока foo вернёт результат(ели ещё не вернула)
CAB>  ..
CAB>


Это
1. Много где называется promise http://en.wikipedia.org/wiki/Futures_and_promises
2. Как только это появляется в коде, код становится недетерминированным (когда primise вернет результат? а никому не известно)

CAB> Когда появляются общие переменные всё становится несколько сложнее, например:

CAB> Для чего я хочу использовать хитрый алгоритм синхронизации для общих переменных:

Все. Забудь о параллелизации. Вспоминай дедлоки и т.п.

CAB> У каждой общей переменной может быть только одна изменяющая её функция, т.е. один писатель много читателей(это ограничения для оптимизации).

CAB>Каждая функция-писатель является критической секцией и захватывает записываемые переменные на время своего выполнения, кроме того пред захватом она читает все требуемые общие переменные, т.е это примерно эквивалентно передаче их через аргументы, например для foo из пример выше:

Ага. Часть из эти переменных будет promise'ами. И обновится они не смогут, когда придут данные, потому что их уже захватила критическая секция.

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


Эрланг спокойно живет и с множеством потоков и с копированием значений. Потому что
1. Потоки не имеют отношения к потокам системы
2. Все данные иммутабельны
3. Так как данные иммутабельные, можно копировать/передавать только указатели на данные в памяти

CAB>Это будет работать? Как по вашему, сложно писать в таком стиле(когда каждый вызов это отдельный поток)?


Да, можно. Но обычно не нужно.


dmitriid.comGitHubLinkedIn
Re[3]: Многопоточность
От: Mamut Швеция http://dmitriid.com
Дата: 01.05.13 10:23
Оценка: :)
KA>>Скорости, масштабируемости ?
CAB>Список приоритетов выглядит примерно так: простота, надёжность, масштабируемость, скорость.

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

KA>>Какие проблемы решит ?

CAB>Прежде всего проблему освоения вычислительной мощности современных процессоров (разробы процессоров вдохнут с облегчением ).

Erlang смотрит на такие статьи с недоумением.


dmitriid.comGitHubLinkedIn
Re[5]: Многопоточность
От: C.A.B LinkedIn
Дата: 01.05.13 10:39
Оценка:
CAB>Можно так реализовать:
Ошибка, правильно так:
  //Очередь сообщений
  def q = LIST[Strin]
  //Поток-отправитель
  ...
  while(...){
    ...
    send("Some message")
    ...
  }
  ...
  def send = {m:String) //Функция-писатель
    q := q :: m
  (}
  //Поток-получаетль
  ...
  while(...){
    if isEmpty() => sleep(50)
    else => ... //Обработка 
  }
  ...
  def isEmpty = {)  
     size(q) == 0
  (Bool}


И таки вы, AlexRK, правы, это подход(синхронизация общих переменных) не спасает от деделоков:
  var v1 = 0
  doer a = {)
    while(ra()){   //Будет крутится и ждать v1 == 0
      ...
    }
    v1 := 1
  (}
  def ra = {)
    v1 == 0
  (Bool}
  doer b = {)      //Будет крутится и ждать v1 == 1
    while(rb()){
      ...
    }
    v1 := 1
  (}
  def rb = {)
    v1 == 1
  (Bool}

Печально
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re[2]: Многопоточность
От: C.A.B LinkedIn
Дата: 01.05.13 11:22
Оценка:
Здравствуйте, Mamut, Вы писали:
M>2. Как только это появляется в коде, код становится недетерминированным (когда primise вернет результат? а никому не известно)
Какая разница когда она его вернёт(кроме того что вызвавший поток остановится не в месте вызова, а в месте где потребуется результат)?
CAB>>Каждая функция-писатель является критической секцией и захватывает записываемые переменные на время своего выполнения, кроме того пред захватом она читает все требуемые общие переменные, т.е это примерно эквивалентно передаче их через аргументы, например для foo из пример выше:
M>Ага. Часть из эти переменных будет promise'ами. И обновится они не смогут, когда придут данные, потому что их уже захватила критическая секция.
Только одна функция может изменять некоторую общую переменную, она же является критической секцией, т.е. переменные обновляются внутри критической секции, а но не вне.
M>Эрланг спокойно живет и с множеством потоков и с копированием значений. Потому что
M>1. Потоки не имеют отношения к потокам системы
M>2. Все данные иммутабельны
M>3. Так как данные иммутабельные, можно копировать/передавать только указатели на данные в памяти
Как, в нескольких словах, в Эрланге реализовано например такое:
  def apendOne(hm:MyHighArray):MyHighArray = {
    hm.add(1)
  }

Т.е. когда большая структура немного изменяется, и получается вторая большая структура. Как оптимизировано копирование.
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
Re[3]: Многопоточность
От: Mamut Швеция http://dmitriid.com
Дата: 01.05.13 18:41
Оценка:
M>>2. Как только это появляется в коде, код становится недетерминированным (когда primise вернет результат? а никому не известно)
CAB>Какая разница когда она его вернёт(кроме того что вызвавший поток остановится не в месте вызова, а в месте где потребуется результат)?

Скобки отвечают на твой вопрос

CAB>>>Каждая функция-писатель является критической секцией и захватывает записываемые переменные на время своего выполнения, кроме того пред захватом она читает все требуемые общие переменные, т.е это примерно эквивалентно передаче их через аргументы, например для foo из пример выше:

M>>Ага. Часть из эти переменных будет promise'ами. И обновится они не смогут, когда придут данные, потому что их уже захватила критическая секция.
CAB>Только одна функция может изменять некоторую общую переменную, она же является критической секцией, т.е. переменные обновляются внутри критической секции, а но не вне.

Если это общие переменные, то...?

CAB>Как, в нескольких словах, в Эрланге реализовано например такое:

CAB>
CAB>  def apendOne(hm:MyHighArray):MyHighArray = {
CAB>    hm.add(1)
CAB>  }
CAB>

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

Массив == список, выглядит как [cons [cons [cons [cons [nil]]]]], http://en.wikipedia.org/wiki/Cons

Добавление нового элемента в конец равнозначно:
1. Создаем новый cons cell, содержащий 1
2. Переписываем два указателя:
2.1 С последнего cons списка на новый cons.
2.2 С нового cons списка на nil

Все.

Совету дождаться этой книги: http://stenmans.org/happi_blog/?p=173 Там очень много деталей о реализации всего это дела Часть подробностей есть тут: http://www.it.uu.se/research/publications/reports/2000-029/ и тут: http://erlanger.ru/ru/page/2121/efficient-memory-management-for-message-passing-concurrency


dmitriid.comGitHubLinkedIn
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.