Многопоточная работа из dll на C с Lua

Страницы: 1
RSS
Многопоточная работа из dll на C с Lua
 
Добрый день, столкнулся с такой проблемой в последнее время:
думал чтобы поменьше грузить Quik, запустить внутри lua, через средства C отдельный поток std::thread и в нём спокойно обрабатывать запросы стороннего приложения, некий сервер квик-данных реализовать.

Но наткнулся на неприятную проблему, несмотря на то что потковая функция, pipe_thread спокойно работает, если её исполнить в основном main потоке (там тот же самый цикл по while (!stopped)), когда я запускаю её в отдельном потоке, я вижу как квик начинает тормозить: медленно открываются все окна, время начинает драматически отставать, и вообще происходят странные вещи: открытый квик и открые часы Windows 10 начинают "замирать", т.е. показывают секунды 1, 2, 9, 15, вместо 1, 2, 3, 4 ...

Может быть кто-нибудь сталкивался с этим? Не было ли в последней версии каких-то критических изменений связанных с синхронизацией и многопоточностью?
Сейчас код выглядит так:
Код
int main(lua_State *L){
   //// тут мы создаём новый state для отдельного потока
   //// см https://kristianrumberg.wordpress.com/2010/11/21/135/
   //// https://stackoverflow.com/questions/17817452/lua-5-2-2-broken-threading-system/17818481#17818481
   //// https://forum.quik.ru/messages/forum10/message10476/topic212/#message10476

   //lua_gc(L, LUA_GCSTOP, 0);
   //lua_State *tL = lua_newthread(L); // здесь tl кладётся в т.ч. стек L
   //int thread_L_ref = luaL_ref(L, LUA_REGISTRYINDEX); // создаётся ссылка на объект на верхушке стека
   //lua_pushvalue(L, -1);
   //lua_gc(L, LUA_GCRESTART, 0);

   //stopped = false;
   //std::thread pipeThread(pipe_thread, tL);
   //lua_function<lua_Number> lua_Sleep(L, "sleep");

   //while (!stopped) {
   //   lua_Sleep( 1000 );
   //}

   //pipeThread.join();
   //lua_unref(L, thread_L_ref);
   pipe_thread(L);
   message(L, "QPIPE: скрипт остановлен");

   return 0;
}



Если закомментировать вызов pipe_thread и расскомментировать код, который создаёт отдельный поток, то будут тормоза.
Может быть кто-нибудь решал похожую задачу?
 
Не знаю насколько моя задача похожая, поделюсь наблюдениями вдруг чем то поможет.
Новые потоки из С-функции зарегистрированой в луа-движке как main пробовал. Но STL не пользовался, пробовал штатное средство _beginthreadex и пробовал boost thread. В созданом потоке пробовал крутить просто цикл наподобие вашего while (!stopped), цикл ожидания каких нибудь событий по WaitForSingleObject(...) а также пробовал запускать интерфейс на WinForms. Описаной вами проблемы не наблюдалось. Думаю можно сказать что ожидаемо все работало.

У меня при первом взгляде на ваш код возникает такой вопрос, а как там "внутри" устроена функция lua_Sleep(), может в ней проблема при вызове из 2 потоков? Попробуйте вместо нее
Код
#include <Windows.h>
Sleep(1000);
или
Код
#include <chrono>
#include <thread>
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
Других мыслей навскидку нет ))
 
Цитата
BlackBoar написал:
Не знаю насколько моя задача похожая, поделюсь наблюдениями вдруг чем то поможет.
Новые потоки из С-функции зарегистрированой в луа-движке как main пробовал. Но STL не пользовался, пробовал штатное средство _beginthreadex и пробовал boost thread. В созданом потоке пробовал крутить просто цикл наподобие вашего while (!stopped), цикл ожидания каких нибудь событий по WaitForSingleObject(...) а также пробовал запускать интерфейс на WinForms. Описаной вами проблемы не наблюдалось. Думаю можно сказать что ожидаемо все работало.

спасибо за отзыв!
я сейчас "грешу" на всё подряд, вот как раз думаю про WaitForSingleObject - нет ли в ней подводных камней?
или может быть проблема в том что я использую time_beginperiod(1)?
у меня использовались и простые Sleep тоже, я подумал что лучше максимально прикинуться "Lua", чтобы квику было "хорошо"

не могу понять что не так с кодом, какие есть запрещённые функции.
я ведь правильно понимаю, что если я в main(L) буду использовать какой-нибудь ReadFile блокирующий, то это нормально, т.к. ожидание будет "в ядре"?
у меня есть цикл, я там использую примерно такой вариант
Код
         if (!ReadFile(handle, readBuffer, BUFFER_SIZE, &bytesRead, &overlapped)) { // асинхронное чтение, возврат по ERROR_IO_PENDING - надо ждать завершения
            DWORD error = GetLastError();
            if (error == ERROR_IO_PENDING) {
               message(threadL, "Ждём записи");
               DWORD status = WaitForSingleObject(handle, 1000); // ниже проверки на успех или обрыв, обычно читает
...
processCommand(threadL, readBuffer, bytesRead); // тут всякие квик-луа запросы
...
if (!WriteFile(handle, responseBuffer, responseLength, &written, &overlapped)) {
...// тут проверка на обрыв и дисконнект - обычно проходит мимо
}

// дальше идём на ReadFile, там обычно ловим IO_PENDING и входим в WaitForSingleObject
может ли этот цикл чего-то "ломать"?
я проверял через lua_gettop() в конце каждой итерации записи - в стеке пусто, как и должно быть.
 
Цитата
Павел Bosco написал:
я сейчас "грешу" на всё подряд, вот как раз думаю про WaitForSingleObject - нет ли в ней подводных камней?
Наверняка полно но врядли там есть что-то что могло бы спровоцировать описаную вами ситуацию. WaitForSingleObject исправно выкидывает ждущий поток с процессора а у вас по описанию наоборот выглядит, какие-то потоки до упора его занимают.

Цитата
Павел Bosco написал:
или может быть проблема в том что я использую time_beginperiod(1)?
А это что? Если имеется в виду MMRESULT timeBeginPeriod(   UINT uPeriod ); то да, есть в МСДН такая интересная фраза

Цитата
Use caution when calling timeBeginPeriod, as frequent calls can significantly affect the system clock, system power usage, and the scheduler.
Полный текст здесь, https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-sleep

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

Поскольку меня этот вопрос тоже интересует, создал на пробу 3 таких потока. У меня они как и задумано раз в секунду флудят в окно сообщений квика, остальное время исправно спят. Загрузка процессора 0, ничего не тормозит. Если не затруднит скомпилируйте и попробуйте у себя, весь код под спойлером.

Скрытый текст


Если найдете причину опишите плиз, тоже интересно знать чего не надо делать )))
 
Цитата
BlackBoar написал:

Если найдете причину опишите плиз, тоже интересно знать чего не надо делать )))
спасибо ещё раз, за трезвый взгляд.
я повыкидывал timeBeginPeriod, кажется получше стало. с холостым ходом тоже решил проблему, поменяв Sleep на WaitForSingleObject
сейчас есть некоторое замедление только на старте сессии, в 10:00 - 10:30, дальше вроде тоже есть неравномерность, на секундочку бывает замирает, частенько, надо последить ещё.
возможно я забыл перезагрузиться после выключения timeBeginPeriod, сейчас после перезагрузки уже 11:50 и всё тикает равномерно.

или думаю, может это потому что я слишком бомбардирую квик?
есть ли интересно какой-нибудь предел обращения в цикле, допустим к свечкам DataSource, я ведь из C++ довольно быстро это делаю.
какую паузу между перебором свечей в DS резонно вставить, 10 миллисекунд кажется мало?
может надо создать какой-нибудь глобальный именной event в квике и взводить его, когда изменяется свечка только в updateCallBack, а в другом процессе его ловить.
 
Цитата
Павел Bosco написал:
может надо создать какой-нибудь глобальный именной event в квике и взводить его, когда изменяется свечка только в updateCallBack, а в другом процессе его ловить.
Я так и делаю, только событий у меня много, поэтому

WaitForMultipleObjects с сопутствующими функциями  
 
Цитата
Павел Bosco написал:
я повыкидывал timeBeginPeriod, кажется получше стало. с холостым ходом тоже решил проблему, поменяв Sleep на WaitForSingleObjectсейчас есть некоторое замедление только на старте сессии, в 10:00 - 10:30, дальше вроде тоже есть неравномерность, на секундочку бывает замирает, частенько, надо последить ещё.возможно я забыл перезагрузиться после выключения timeBeginPeriod, сейчас после перезагрузки уже 11:50 и всё тикает равномерно.
Чтож, будем знать про потенциально возможные грабли с timeBeginPeriod.
Ну а тупому слипу в рабочем коде имхо в любом случае не место, так только, для тестов или проблему выявить.
Замедление в 10 утра (опять же имхо) может быть вполне нормальным если много чего обрабатываете.

Цитата
Павел Bosco написал:
или думаю, может это потому что я слишком бомбардирую квик?есть ли интересно какой-нибудь предел обращения в цикле, допустим к свечкам DataSource, я ведь из C++ довольно быстро это делаю.
Эксперимента ради бомбил квик миллионами запросов подряд, только у меня запрашивались getParamEx2 и getQuoteLv2. Поток который этим занимался естественно грузил 1 процессорное ядро на полную (это очевидно так и быть должно), каких-то аномалий / ошибок при получении результатов не нашел. Что будет если начать гонять функциональность квик сразу из нескольких потоков сам пока не проверял. Спрашивал в соседней теме разрабов, с их слов вроде должно работать параллельно.

Если у вас по этому поводу получаются другие данные тоже было бы интересно узнать что именно.
 
Ну до миллионов у меня дело не дошло.
Я всё-таки пока остановился на слипах в цикле на клиенте. Ну может и не красиво, пока вроде бы работает.
Скорость выбрал ~350 сообщений в секунду. Так работает. Мне кажется больше от квика требовать смысла нету.

Честно говоря мне немного удивительно что вы бомбардровали квик и он не наелся.
Я спрашиваю getParameEx, и опрашиваю datasource (который создаю один раз).
По datasource имеется цикл, так что это наверное потяжелее операция.
Так же смотрю таблицы лимитов и позиций, там тоже небольшие циклы есть.  
 
Цитата
Борис Гудылин написал:
Я так и делаю, только событий у меня много, поэтому

WaitForMultipleObjects с сопутствующими функциями
вам случайно не приходилось решать проблему с определением того, когда можно считать "свечку" готовой, что она уже больше не обновится?
интересно если её можно решить максимально "близко" по времени и идеально точно по данным.
 
BlackBoar,
Цитата
BlackBoar написал:
Что будет если начать гонять функциональность квик сразу из нескольких потоков сам пока не проверял.
Я проверял что-то, там был нюанс
https://forum.quik.ru/messages/forum10/message29998/topic3409/#message29998
 


может дело было вообще не в бобине
вот так на 8 ядерном CPU последняя версия квика работает в чистом виде, без всяких запущенных скриптов

почему?..
явно ведь что такую штуку "наесть" легче лёгкого..
 
Цитата
Павел Bosco написал:
Ну до миллионов у меня дело не дошло.
Это было что-то типа стресс-теста. По дороге еще мерил что сколько времени занимает. В принципе сделал для себя ряд полезных наблюдений. В реальной то жизни у меня пока активность невысокая.


Цитата
Павел Bosco написал:
Честно говоря мне немного удивительно что вы бомбардровали квик и он не наелся.
Ну у меня же осуществлялся доступ к хранилищу терминала в режиме "только чтение", там по идее должна быть очень недорогая синхронизация. То есть это я так думаю что должно быть по идее только чтение, исходников терминала не видел разумеется. Но результат вроде адекватен.

Цитата
Павел Bosco написал:
вот так на 8 ядерном CPU последняя версия квика работает в чистом виде, без всяких запущенных скриптов
К сожалению у меня ваш скрин открывается очень размытым каким-то, нифига не разобрать. Просто для сравнения, у меня квик в котором я торгую "традиционным образом", там тоже нет никаких пока скриптов и тп. Но присутствует в общей сложности порядка 350 окон (таблицы / графики). На 50 с чем то вкладках. Так вот, памяти он жрет по меркам 32 бит процесса немеряно, к вечеру обычно порядка 2.2-2.4 гига нажирает. Но проц грузит очень умеренно, если на виду 5 графиков то гдето на треть одно ядро. А если на виду нет графиков вообще не грузит почти. Данные в нем заказаны в максимально возможном количестве (для справки).

Цитата
Борис Гудылин написал:
Я проверял что-то, там был нюанс https://forum.quik.ru/messages/forum10/message29998/topic3409/#message29998
Это немного другой нюанс, хотя он из той же темы и тоже заслуживает пристального внимания. Читал, имею в виду. Ну опять же, многопоточныых стресс-тестов пока не гонял, своих комментариев нет.
Меня изначально интересовала ситуация примерно такого плана, допустим стаканы я получаю в мэйн поскольку эта операция относительно дорогая а всякие там лучшие биды и тп сразу в коллбэке. Вот не возникнет ли каких-то затыков из-за внутренней синхронизации в квике. Официальный ответ вроде нет и по моим представлениям вроде так и быть должно. Как дойду до обработки стаканов обязательно проверю, пока другим занят ))
 
картинка вроде бы подгружается с улучшением качества, сначала плохое, потом загружается и становится отличной
у меня так :)

вобщем сделал профилирование, по-моему я убедился, что проблема не в моём коде
вот моя библиотечка - кушает немного совсем, на общем фоне не видно, ничего не лочит


скорее всего дело в графиках.
удалил несколько ADX и RSI, посмотрю в понедельник. на вечёрке всё летает.
квик у меня только один, и для скриптов и просто посмотреть.
видимо что-то криво в графиках написано у разрабов - всё работает в одном потоке! потому что на одном ядре.

 
Цитата
Павел Bosco написал:
видимо что-то криво в графиках написано у разрабов - всё работает в одном потоке! потому что на одном ядре.
Вообще  всегда считал что это хотя и не обязательное требование, но уж как  минимум обычная практика для вендовых программ, все окна в одном потоке.  Иначе с диспетчирезацией сообщений + синхронизацией очень много гемора  если в 2 словах. Возможный выигрыш производительности редко того стоить  может.
Нам остается эту данность учитывать ))
 
Если графики нужны чисто боту их можно посворачивать, будет уже намного лучше.
А если нужны себе для "традиционной" ручной торговли, можно альтернативным квиком обзавестись открыв где-нить второй чем на символическую сумму. У меня в сбере такой есть )))
Такая вот мысль возникла...
 
вобщем углубился немного в профилирование, узнал даже что-то новое и интересное.
и по-моему нашел баг. даже чуть глубже чем в квике.

monsters inc scare simulator
если я всё правильно понимаю, квик пытается что-то полупрозрачное "отрисовать" и при этом отчаянно подвисает в win32kfull.sys
это 32 битный драйвер десктопа (системный компонент windows) и обновлён у меня он был 28.08.2018
у меня было много обновлений установлено 29 августа.
решил обновить windows, у меня была старенькая версия, в новой версия драйвера поновоее.
вот такой драйвер у меня

monsters inc scare simulator

надеюсь проблема решится. вообще больше похоже было на общесистемную проблему. потому что часы десктопные замирали.
 
извиняюсь за ссылки под картинками. кажется сайт хостинга картинок пропихнул рекламу.
к сожалению редактировать сообщения нельзя.
 
Цитата
Павел Bosco написал:
Добрый день, столкнулся с такой проблемой в последнее время:
думал чтобы поменьше грузить Quik, запустить внутри lua, через средства C отдельный поток std::thread и в нём спокойно обрабатывать запросы стороннего приложения, некий сервер квик-данных реализовать.

Но наткнулся на неприятную проблему, несмотря на то что потковая функция, pipe_thread спокойно работает, если её исполнить в основном main потоке (там тот же самый цикл по while (!stopped)), когда я запускаю её в отдельном потоке, я вижу как квик начинает тормозить: медленно открываются все окна, время начинает драматически отставать, и вообще происходят странные вещи: открытый квик и открые часы Windows 10 начинают "замирать", т.е. показывают секунды 1, 2, 9, 15, вместо 1, 2, 3, 4 ...

Может быть кто-нибудь сталкивался с этим? Не было ли в последней версии каких-то критических изменений связанных с синхронизацией и многопоточностью?
Сейчас код выглядит так:
Код
  int main(lua_State  * L){
   //// тут мы создаём новый state для отдельного потока
   //// см https://kristianrumberg.wordpress.com/ 2010 / 11 / 21 / 135 /
   //// https://stackoverflow.com/questions/ 17817452 /lua -  5  -  2  -  2  - broken - threading - system/ 17818481  #  17818481 
   //// https://forum.quik.ru/ message s/forum10/ message 10476/topic212/ #  message 10476

   //lua_gc(L, LUA_GCSTOP,  0 );
   //lua_State  * tL  =  lua_newthread(L); // здесь tl кладётся в т.ч. стек L
   //int thread_L_ref  =  luaL_ref(L, LUA_REGISTRYINDEX); // создаётся ссылка на объект на верхушке стека
   //lua_pushvalue(L,  -  1 );
   //lua_gc(L, LUA_GCRESTART,  0 );

   //stopped  =   false ;
   //std::thread pipeThread(pipe_thread, tL);
   //lua_function < lua_Number >  lua_Sleep(L,  "sleep" );

   // while  ( ! stopped) {
   //   lua_Sleep(  1000  );
   //}

   // pipeThread.join ();
   //lua_unref(L, thread_L_ref);
   pipe_thread(L);
    message (L,  "QPIPE: скрипт остановлен" );

    return   0 ;
}



  
Если закомментировать вызов pipe_thread и расскомментировать код, который создаёт отдельный поток, то будут тормоза.
Может быть кто-нибудь решал похожую задачу?
давно это было. делал доп потки. ничего не тормозило.
Если не читали,
то советую проштудировать
Джеффри Рихтер .Создание эффективных WIN32 приложений с учетом специфики 64 разрядной версии Windows.
Страницы: 1
Читают тему
Наверх