+ Ответить в теме
Показано с 1 по 15 из 15
  1. #1

    Потоки Threads

    Хотелось бы понять, как правильно работать с потоками. Желательно на самом низком возможном уровне. На данный момент в SoulEngine определены следующие функции для работы с потоками:

    thread_code
    thread_count
    thread_enginefile
    thread_free
    thread_include_enc2
    thread_init
    thread_loaded
    thread_priority
    thread_resume
    thread_run
    thread_start
    thread_stop
    thread_suspend
    thread_terminate
    thread_terminateandwaitfor
    thread_var
    thread_working
    threads_vars

    Можно увидеть хотя бы краткое описание каждой функции в одну строчку?
    Например, я не могу понять как работает функция thread_stop, не могу понять для чего нужна функция thread_run, не могу понять, почему моя программа словно зависает, если я вызываю thread_free($thread_id) в основном потоке (если вызываю внутри потока $thread_id, то всё работает).

    У меня возникла задача останавливать поток извне. Пробовал много вариантов методом тыка. Единственный вроде бы внешне рабочий как мне надо вариант - это вызов thread_suspend($thread_id). И к моему удивлению это работает на ура. Поток реально замораживается моментально, и при закрытии приложения не выскакивает никаких левых фатальных ошибок.

    Но вот что меня смущает... thread_suspend замораживает поток, а не останавливает. И его потом можно продолжить методом thread_start или thread_resume (второй не проверял, но по идее работать должно по своему назначению). Но как мне полностью остановить поток и по возможности выгрузить из памяти?

    До этого я делал переменную-флаг, и передавал её при помощи функции v(), если переменная true, то делал break внутри главного цикла в потоке, а после цикла thread_free($thread_id). И это работало! Поток вроде как выгружался из памяти, так как при следующем вызове thread_init('') (кстати, что нужно передавать внутрь этой функции?), она возвращала новый $thread_id. Иначе же thread_init('') вернёт уже использованный, но отработанный до этого $thread_id.

    И я бы использовал этот вариант и дальше. В Java обычно так и делают, потому что поговаривают, что suspend там работает криво. Но внешне для пользователя приложения это выглядит некрасиво. Поток завершает свою работу спустя какое-то время после нажатия на кнопку. Кроме того, если закрыть приложение на крестик главного окна в то время, когда работает в фоне поток, то выскакиевает фатальная ошибка с просьбой отправить отчёт и ещё одна по поводу того, что я вызываю метод компонента формы из несуществующего объекта. Вторую ошибку можно побороть, если определить событие onCloseQuery у главной формы, сделать там сокрытие формы через метод hide(), потом запрос на остановку потока через переменную-флаг и sleep на несколько секунд. Фатальная же ошибка всё равно остаётся. А вот вариант с thread_suspend работает идеально вроде как, но я боюсь, как бы не получилось проблем, если придётся стартовать новый поток и останавливать много-много раз. То есть после thread_suspend надо бы сделать thread_free, но, как я сказал выше, почему-то вызов этой функции вне потока вешает приложение.

    Ну и логичный, как мне кажется, вопрос: почему функция thread_stop не работает? Или она работает не так, как я предполагаю. Я уж не говорю про другие экзотические функции типа thread_terminateandwaitfor, которые и подавное непонятно как и для чего использовать.

    Надеюсь услышать ответ хотя бы по поводу suspend/stop и о том, как правильно останавливать и выгружать из памяти поток извне (из основного или параллельного потока).

  2. #2
    Админ Аватар для vGhost
    Регистрация
    27.07.2011
    Адрес
    Самара
    Сообщений
    2,033
    Вот, можете посмотреть код как я работал, это не низкий уровень(не совсем понятно зачем вам именно низкий уровень), но чтоб придти к такому решению не одну ночь не спать пришлось.
    http://community.develstudio.ru/atta...0&d=1314949577

    ds очень капризна по части потоков я пока писал код, раза 3 его структуру с нуля переписывал. Вобщем из найденных мной камней:
    1) не используйте алгоритм "создал запустил поработал уничтожил(free)", ds не освобождает память после потоков. Будет утечка памяти. Если использовать алгоритм "создал запустил поработал уничтожил(free)" где то раз 2000, это отожрёт 4 гига оперативки.
    2) алгоритм "создал запустил поработал(, заново запустил, с новыми данными, поработал)* N раз" тоже не реккомендую. Будут "плавающие баги" совершенно в не ожиданных местах, будут теряться ф-и, могут даже потеряться целые ссылки на треду, где то после 6000 раз когда так сделаете. Его можно использовать безопасно если потоков будет не много и за время работы программы N не будет больше 1000

    Единственный пока что безглючный вариант который я нашёл это чтоб запущенный поток на время работы программы "не когда не кончался", когда ему нечего делать пускай находится в состоянии suspend или тупо ничего не делает т.е. код потока аля:
    PHP код:
    while(true)
    {
       do
       {
          
    получаем данные через v();
          
    delay(1000);
       }while(
    нет данных для обработки)

       
    оборабатываем полученные данные

    Но suspend лучше.

    --------------------------------


    Цитата Сообщение от Phantom Посмотреть сообщение
    У меня возникла задача останавливать поток извне. Пробовал много вариантов методом тыка. Единственный вроде бы внешне рабочий как мне надо вариант - это вызов thread_suspend($thread_id). И к моему удивлению это работает на ура. Поток реально замораживается моментально, и при закрытии приложения не выскакивает никаких левых фатальных ошибок.
    1) насколько я понял stop тупо ничего не делает, толи это баг. толи код этой процедуры ещё просто не реализован, но остановить у меня не вышло как не пытался.
    2) suspend не останавливает треду, он её замораживает, потом она продолжает работу с того места где была во время приостановки.
    Чтоб остановить треду я пользовался кодом
    PHP код:
    while(v('ThreadsWorkingOn'))
    {
       
    делаем чёта полезное но не большое
       
    If(!v('ThreadsWorkingOn'))
       {
           Break;
       }
       
    делаем чёта полезное но не большое
       
    If(!v('ThreadsWorkingOn'))
       {
           Break;
       }
       
    делаем чёта полезное но не большое
       
    If(!v('ThreadsWorkingOn'))
       {
           Break;
       }
       
    делаем чёта полезное но не большое

    Данным кодом вы убьёте двух зайцев, первый прекратит работу когда вы установите v('ThreadsWorkingOn', false) в основном коде программы. Во вторых вы никогда, ещё раз вопторяю вы не когда не прервёте треду не дав ей не корректно завершиться, например скажем во время перзаписи файла с данными сделав его повреждённым и не полным, т.к сами втыкаете в код "брэйки" где треда по требованию может безопасно завершиться. (в том примере выше что я дал ссылку, нет такого кода, о котором щас реч веду, в виду особенности той программы)


    Цитата Сообщение от Phantom Посмотреть сообщение
    Но вот что меня смущает... thread_suspend замораживает поток, а не останавливает. И его потом можно продолжить методом thread_start или thread_resume (второй не проверял, но по идее работать должно по своему назначению). Но как мне полностью остановить поток и по возможности выгрузить из памяти?
    Правильнее использовать resume

    Цитата Сообщение от Phantom Посмотреть сообщение
    Но внешне для пользователя приложения это выглядит некрасиво. Поток завершает свою работу спустя какое-то время после нажатия на кнопку.
    В этом нет ничего не красивого, создайте "статус строку" и напишите пользователю "завершаю работу". Это правильный выход, с точки зрения целостности информации. Весь софт что я видел и который работает с какими то данными так делает, потоки не когда не завершаются сиюжесекунду по нажатию кнопочки отмета. Самый простой тому пример установщик любой программы, ставил пользователь её, ставил, потом передумал и нажал отмена. Если установщик прервёт работу сиже секунду. что будет у пользователя на компе? Куча мусора от недоустановленной программы. Так что это вполне нормально если поток завершится не сразу. Могу кинуть пример такой реализации.


    Цитата Сообщение от Phantom Посмотреть сообщение
    Кроме того, если закрыть приложение на крестик главного окна в то время, когда работает в фоне поток, то выскакиевает фатальная ошибка с просьбой отправить отчёт и ещё одна по поводу того, что я вызываю метод компонента формы из несуществующего объекта.
    Вообще по хорошему надо блокировать такие нажатия во время работы потоков и при нажатии сначала запускать механизм остановки потоков (v(...)) а потом позволять приложению завершится, иначе вы рискуете получить испорченные/не завершённые данные и файлы, если ваша программа с чем то работает.

    Цитата Сообщение от Phantom Посмотреть сообщение
    Ну и логичный, как мне кажется, вопрос: почему функция thread_stop не работает? Или она работает не так, как я предполагаю. Я уж не говорю про другие экзотические функции типа thread_terminateandwaitfor, которые и подавное непонятно как и для чего использовать.
    Их либо не реализовали до конца. либо там просто баг


    З.Ы. если уж очень сильно хотите посмотреть как эти низкоуровневые процедуры юзать, можете посмотреть файл \PHP Devel Studio 2.0 beta\engine_sdk\main\threads.php там находится класс Thread
    Последний раз редактировалось vGhost; 14.09.2011 в 13:07.

    __________________________________________________ ________
    Угу, угу... А потом достал из морозильной камеры - и степлером его, степлером!
    Для новичков
    __________________________________________________ ________
    Я практически совсем не задаю вопросов, не думали почему? Потому что я пользуюсь поиском и всегда нахожу ответы на свои вопросы!

  3. #3
    Я читал вашу тему про потоки. И вообще вроде бы всё, что есть на форуме, прочитал. Класс Threads тоже весь перерыл. Мне нужно именно остановить поток моментально по запросу пользователя. И пофиг, что именно происходило в потоке. Никаких проблем с недописанными файлами или чем-то таким не будет. Я говорю про конкретный проект.
    Код такого типа:
    PHP код:
       If(!v('ThreadsWorkingOn')) 
       { 
           Break; 
       } 
    я и использовал изначально.

    По поводу утечки ресурсов и того, что потоки не выгружаются полностью из памяти. Я проводил тесты и вроде как тот же suspend не вызывает утечки памяти при последовательном запуске и приостановке нескольких потоков. Конечно 2000 я запускать не пробовал. Наверно ещё проведу тесты.

  4. #4
    Админ Аватар для vGhost
    Регистрация
    27.07.2011
    Адрес
    Самара
    Сообщений
    2,033
    Цитата Сообщение от Phantom Посмотреть сообщение
    Я читал вашу тему про потоки. И вообще вроде бы всё, что есть на форуме, прочитал. Класс Threads тоже весь перерыл. Мне нужно именно остановить поток моментально по запросу пользователя. И пофиг, что именно происходило в потоке. Никаких проблем с недописанными файлами или чем-то таким не будет. Я говорю про конкретный проект.
    В таком случае используйте suspend это заморозит поток моментально. Но вот что с ним дальше делать, я чёт пока не придумал.

    Цитата Сообщение от Phantom Посмотреть сообщение
    Я проводил тесты и вроде как тот же suspend не вызывает утечки памяти при последовательном запуске и приостановке нескольких потоков.
    Нет, не вызывает, но он и не останавливает поток, он его замораживает.
    Остановка потока это прерывание выполнения его кода по типу как exit в php скриптах. А suspend это как sleep();
    Если бы stop работало, то после старта поток начал бы работу сначала, а после suspend он продолжит с того места где он был в момент выполнения suspend. Так что это совершенно разные вещи.


    Но я не совсем знаю вашу задачу, возможно вам то как раз в ней нужно именно suspend заюзать а не stop.

    __________________________________________________ ________
    Угу, угу... А потом достал из морозильной камеры - и степлером его, степлером!
    Для новичков
    __________________________________________________ ________
    Я практически совсем не задаю вопросов, не думали почему? Потому что я пользуюсь поиском и всегда нахожу ответы на свои вопросы!

  5. #5
    Да нет, нужно остановить поток насосвсем, удалить его из памяти нафиг и забыть про него. Это в идеале.

  6. #6
    Админ Аватар для vGhost
    Регистрация
    27.07.2011
    Адрес
    Самара
    Сообщений
    2,033
    Тогда ждём ds 3.0 Там обещают будет с этим порядок. А пока MissionImpossible...
    Можно конечно ещё после каждой php инструкции насувать
    PHP код:
    If(!v('ThreadsWorkingOn'))  
    {  
          Break;  

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

    __________________________________________________ ________
    Угу, угу... А потом достал из морозильной камеры - и степлером его, степлером!
    Для новичков
    __________________________________________________ ________
    Я практически совсем не задаю вопросов, не думали почему? Потому что я пользуюсь поиском и всегда нахожу ответы на свои вопросы!

  7. #7
    Админ Аватар для vGhost
    Регистрация
    27.07.2011
    Адрес
    Самара
    Сообщений
    2,033
    Кстати, вот ещё мысль. только что пришла в голову.
    Cделать 2 программы, основная - интерфейс пользователя и всё такое.
    Вторая программа, это то что у вас должно быть в потоках. Из первой программы тупо запускать вторую. А при нажатии stop тупо делать taskkill /F IM ChildProg.exe

    __________________________________________________ ________
    Угу, угу... А потом достал из морозильной камеры - и степлером его, степлером!
    Для новичков
    __________________________________________________ ________
    Я практически совсем не задаю вопросов, не думали почему? Потому что я пользуюсь поиском и всегда нахожу ответы на свои вопросы!

  8. #8
    Кстати, вот ещё мысль. только что пришла в голову.
    Cделать 2 программы, основная - интерфейс пользователя и всё такое.
    Вторая программа, это то что у вас должно быть в потоках. Из первой программы тупо запускать вторую. А при нажатии stop тупо делать taskkill /F IM ChildProg.exe
    Неее, это мегакостыль.

  9. #9
    Главный Разработчик Аватар для Devel
    Регистрация
    11.03.2010
    Сообщений
    1,231
    Эти функции нельзя использовать напрямую, они используются для класса Thread, вот его и нужно использовать.

    Код:
    $t = new Thread;
            $t->code = '
           
                err_no();
                $file_info = file("http://develstudio.ru/upd/last.txt");
                $last_ver = $file_info[3];
                
                    c("fmMain")->lastVer  = $last_ver;
                    c("fmMain")->fileInfo = $file_info;
            ';
    
            $t->start();

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

  10. #10
    Админ Аватар для vGhost
    Регистрация
    27.07.2011
    Адрес
    Самара
    Сообщений
    2,033
    Цитата Сообщение от Devel Посмотреть сообщение
    В идеале нужно лишь подавать сигнал потоку о том, что он должен быть завершен, а уже поток в теле своего выполнения принимает эти сигналы и когда он способен остановиться он останавливается, а не в тот момент когда захотел программист.
    Devel, я как бы в своей программе, во "второй редакции" алгоритма работы с потоками так и делал, более того, даже если не останавливать поток путём отправки ему комманды "тормозни", а он сам завершается полностью отработав, это всё равно в итоге приводило после 6000 таких запусков потоков к совершенно необычным глюкам. Например не с того не с сего процедура, которая перед этим 100500 раз была выполнена, оказовалось что не определена. Часто такими процедурами оказывались процедуры из engine.php кстати. После чего я сделал вывод что потокам лучше вообще не давать завершаться. Пусть они создаются при старте программы и крутятся вечно, а программа когда ей надо чёта в потоках обработать, посылает данные потокам и делает их ->resume(), как поток заврешил обработку данных, отправляет их обратно программе и ->suspend() его, до тех пор пока он снова не понадобится программе.
    Последний раз редактировалось vGhost; 15.09.2011 в 20:35.

    __________________________________________________ ________
    Угу, угу... А потом достал из морозильной камеры - и степлером его, степлером!
    Для новичков
    __________________________________________________ ________
    Я практически совсем не задаю вопросов, не думали почему? Потому что я пользуюсь поиском и всегда нахожу ответы на свои вопросы!

  11. #11
    Главный Разработчик Аватар для Devel
    Регистрация
    11.03.2010
    Сообщений
    1,231
    Цитата Сообщение от vGhost Посмотреть сообщение
    Devel, я как бы в своей программе, во "второй редакции" алгоритма работы с потоками так и делал, более того, даже если не останавливать поток путём отправки ему комманды "тормозни", а он сам завершается полностью отработав, это всё равно в итоге приводило после 6000 таких запусков потоков к совершенно необычным глюкам. Например не с того не с сего процедура, которая перед этим 100500 раз была выполнена, оказовалось что не определена. Часто такими процедурами оказывались процедуры из engine.php кстати. После чего я сделал вывод что потокам лучше вообще не давать завершаться. Пусть они создаются при старте программы и крутятся вечно, а программа когда ей надо чёта в потоках обработать, посылает данные потокам и делает их ->resume(), как поток заврешил обработку данных, отправляет их обратно программе и ->suspend() его, до тех пор пока он снова не понадобится программе.
    Да они никогда не были стабильными. Но вроде новая реализация сделана по канонам многопоточности + пхпдвижок. Ведь тот же апач - многопоточный и наверняка держит тысячи одновременных соединений.

  12. #12
    Админ Аватар для vGhost
    Регистрация
    27.07.2011
    Адрес
    Самара
    Сообщений
    2,033
    Апач с сотней то плохо справляется, на самом деле
    А вот LighTPD да, он и больше 1000 выдержит

    З.Ы. ждём третью версию с нетерпением
    Последний раз редактировалось vGhost; 15.09.2011 в 21:36.

    __________________________________________________ ________
    Угу, угу... А потом достал из морозильной камеры - и степлером его, степлером!
    Для новичков
    __________________________________________________ ________
    Я практически совсем не задаю вопросов, не думали почему? Потому что я пользуюсь поиском и всегда нахожу ответы на свои вопросы!

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

  14. #14
    Админ Аватар для vGhost
    Регистрация
    27.07.2011
    Адрес
    Самара
    Сообщений
    2,033
    Цитата Сообщение от Phantom Посмотреть сообщение
    Я просто хочу разобраться более досконально, чтобы оптимально использовать все предоставленные, а быть может и не документированные, возможности.
    В предверии новой версии, в которой будет полностью переделан механизм работы с потоками, мне что то не кажется это логичным, не находите?

    __________________________________________________ ________
    Угу, угу... А потом достал из морозильной камеры - и степлером его, степлером!
    Для новичков
    __________________________________________________ ________
    Я практически совсем не задаю вопросов, не думали почему? Потому что я пользуюсь поиском и всегда нахожу ответы на свои вопросы!

  15. #15
    В предверии новой версии, в которой будет полностью переделан механизм работы с потоками, мне что то не кажется это логичным, не находите?
    Ну я думаю, что всё таки общие принципы останутся прежними. Да и ждать третьей версии ещё неизвестно сколько, а кодить хочется уже сейчас =)

Метки этой темы

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения