Как стать автором
Обновить

Комментарии 23

конструкцию until..end я убрал из языка

[...]
Представляет она из себя обычный цикл while, с отличием в том, что условие проверяется после выполнения итерации.
Ещё в классическом Паскале (а м.б. ранее) были четко определены циклы с предусловием и постусловием.
while i<5 do P(i)

пока i<5 выполнять процедуру P. Сначала проверяется условие i<5, если оно не выпоняется, то P не будет вызвана ни разу.
repeat P(i) until i<5 

а здесь сначала вызывается P, а только потом проверяется условие i<5, т.о. P всегда будет вызвана (минимум 1 раз).

Спасибо за пояснение.

Для этого мной в Mash был добавлен класс TCriticalSection.

И все? Ни семафоров, ни условных переменных (conditional variables), ни атомиков?
Как-то маловато.

Также возникли вопросы по примеру использования:
proc CriticalThreadedProc():
  while true:
    Sleep(3000)
    CSect -> Enter() // рекурсия поддерживается? Т.е. если я уже лочил CSect в данном треде - сработает или зависнет?

    // а если тут у нас злобный джуниор вписал return - кто будет секцию разлочивать?

    CSect -> Leave()
    gc() // а если в вызывающей функции есть временные переменные - все, им кирдык?
  end
end
Я правильно понимаю, что у вас сопрограммы и потоки — это синонимы? Это все потоки уровня ОС?

Да...

тогда это просто обычные «железные» потоки, которые могут делать некий специальный yield, при котором переходит передача управления в другой заранее заданный «железный» поток. Так? Если так, то ценность такой конструкции крайне сомнительна.
Цель корутин — сохранение состояния выполнения метода. Другого решения этого вопроса я пока что не вижу.
Корутины выполняются в нескольких потоках, которые поочередно замораживают & размораживают друг-друга при вызове yield()

Последнее время немного играл с параллельным программированием в Java. И есть несколько скользких моментов в вашей реализации.


  1. что произойдет при попытке двух потоков задать разное значение атомику? first come, first served?


  2. в случае с критической секцией, что если необходимо дать возможность нескольким потокам одновременный доступ к критической секции?


  3. как насчет коммуникации между корутинами?



и по последнему изменению do..while ->whilst && while..do -> while. в английском, "whilst" — абсолютно то же самое что и "while" (для native speaker'ов; только что уточнил). плюс, разница в двух символах — потенциальный источник примитивнейших ошибок набора текста, при этом разница в поведении — заметная.

1) По очереди будет задано новое значение.
2) Можно передать потокам указатель на класс крит. секции, либо объявить её, как глобальную переменную.
3) TAtomic, ThreadList, глобальные переменные, а также прочие решения на любой вкус и цвет.

Мне кажется, что путаницы не будет :)

По поводу атомика как раз непонятно, в какой очередности будет задано какое значение. Пример:


// bankAccount = 100;


  • Жена, поток 1: bankAccount.set(50)
  • Зарплата, поток 2:bankAccount.set(1500)

В зависимости от очередности, последствия могут быть плачевными ;)

Что мешает делать так:
  • Жена, поток 1: bankAccount->set(bankAccount->get()-50)
  • Зарплата, поток 2: bankAccount->set(bankAccount->get()+1500)


??

дык это не меняет сути вопроса ведь:


  • поток 1: bankAccount->set(bankAccount->get() — 50) === bankAccount->set(100 — 50)
  • поток 2: bankAccount->set(bankAccount->get() + 1500) === bankAccount->set(100 + 1500)

я и спрашиваю: как в этом случае будет разрешен конфликт? какой механизм синхронизации используется внутри атомиков?


для простоты, попробуйте вот такую программу (не знаю, скомпилится ли — ваш проект непонятно ни как собрать вообще, ни как собрать под OSX):


uses <bf>
uses <crt>
uses <threads>

proc addProc(x):
    for (i ?= 0; i < 1000; i++):
        x->set(x->get() + 1)
    end
end

proc subtractProc(x):
    for (i ?= 0; i < 1000; i++):
        x->set(x->get() - 1)
    end
end

proc main():
    bankAccount ?= new TAtomic(0)

    PrintLn("Initial bank account state:", bankAccount->get())

    Async(addProc, bankAccount) // 1000
    Async(subtractProc, bankAccount) // 0
    Async(addProc, bankAccount) // 1000
    Async(subtractProc, bankAccount) // 0
    Async(addProc, bankAccount) // 1000
    Async(addProc, bankAccount) // 2000
    Async(subtractProc, bankAccount) // 1000
    Async(subtractProc, bankAccount) // 0

    PrintLn("Final bank account state:", bankAccount->get()) // а будет ли 0?
end

В java для этого есть операции getAndAdd, addAndGet и подобные. Как в этом языке не знаю но думаю можно добавить на уровне стандартной библиотеки.

Идею понял, в ближайшее время добавлю этот функционал.

Я-то это знаю, мне было интересно как автор предлагает это решить, так как добавление парралелизма в ЯП или его стандартную библиотеку, в моем понимании, подразумевает разрешение подобных вопросов.

Проект можно собрать используя fpc, либо lazarus. Для того, чтобы все заработало, как нужно — собираете svm, библиотеки, mashc и скидываете файлы в похожую, как на гитхабе (bin_w32) иерархию

А что происходит в первом примере с переменной i, новый поток захватывает её текущее значение? В этом случае какая часть контекста копируется в момент создания потока?

В момент создания такого потока копируется состояние переменных внутри тела метода, которые объявлены выше конструкции launch/async. Копии переменных сразу помечаются для сборщика мусора, который имеется у контекста нового потока.
А для массивов и объектов в этом случае копируется содержимое или только указатель?
Для классов — копируется указатель на vtable класса, для массивов — создается новый массив указателей и в него копируются указатели на объекты.

То есть простые параллельные операции над массивами(например сумма элементов), могут быть не эффективным из за издержек на копирование?

Вполне эффективными, если обрабатывать массив частями.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории