@clarabellerising Спасибо помогло.
Запуск приложения в нескольких потоках, но вызов одной функции только в одном потоке
-
У меня есть скрипт, который выполняется в несколько потоков (например, 5) и включает в себя функцию синхронизации файлов из локальной папки с базой данных MySQL. Так вот, запуск этой функции во всех потоках избыточен, так как достаточно, чтобы первый запущенный поток синхронизировал файлы с базой данных, а остальные потоки должны дождаться завершения работы функции синхронизации, вызванной первым пришедшим потоком.
Уже пробовал "Вызвать функцию асинхронно", затем "Дождаться завершения асинхронной функции" и, наконец, получить результат от функции с помощью "Получить результат асинхронной функции", но это не работает. Создается впечатление, что функция все равно вызывается в каждом потоке и, кроме того, результат empy/null.
Как проще всего добиться желаемого поведения?
-
Встроенных средств для этого нет. Название "Вызвать функцию асинхронно" сбивает с толку, сюда больше подходит "Создать новый поток".
Для синхронизации можно использовать глобальную переменную, например в нее записать номер потока хозяина, а остальные потоки пусть ждут освобождение переменной, либо пропускают код. Только нужно учитывать, что в глобальную переменную могут одновременно писать все, поэтому потоку нужно проверять не затерли ли его номер. -
Mutex в BAS нет, к сожалению. Хотя вижу не первый раз запрос на такой функционал.
Глобальные переменные не советую, так как не понятна логика синхронизации между потоками.
@Vituskosoy said in Запуск приложения в нескольких потоках, но вызов одной функции только в одном потоке:
Для синхронизации можно использовать глобальную переменную, например в нее записать номер потока хозяина, а остальные потоки пусть ждут освобождение переменной, либо пропускают код.
Это не будет работать, как ты ожидаешь.
- поток_1 проверяет значение переменной, записано ли там что-то. Если там пусто - пишет свой номер потока.
- поток_2 делает тоже самое
Вот только не исключен вариант, что после проверки значения переменной более одного потока попытаются записать туда свои данные. Так как в момент проверки там было пусто у обоих потоков. Может быть надо проверять после записи, точно ли туда записалось то, что ожидается. Например, если успел записать туда другой поток, то считать, что блокировка не удалась. Вероятно, перед проверкой, что записалось в переменную, надо сделать sleep.
В любом случае городить свои костыли это прямой путь к багам. Нужные атомарные операции должен предоставлять BAS.
-
Это работает много лет:
while (mutex != ThreadIndex) {
if (!mutex) mutex = ThreadIndex;
sleep(1000);
} -
@Vituskosoy said in Запуск приложения в нескольких потоках, но вызов одной функции только в одном потоке:
Это работает много лет:
while (mutex != ThreadIndex) {
if (!mutex) mutex = ThreadIndex;
sleep(1000);
}Проверка значения переменной и последующее присваивание ей значения - не атомарная операция. То, что код написан в одну строчку, ничего не значит, это синтаксический сахар.
И это ровно то, что я описал в предыдущем сообщении.if (!mutex) mutex = ThreadIndex;Использование такого кода - это как переходить дорогу на красный свет, можно делать это тысячи раз и оно "будет работать". Но все понимают, что делать так можно, но не нужно.
-
горе от ума
-
Большое спасибо за то, что поделились своими рекомендациями и опытом. Итак, я думаю, что в моей ситуации лучше всего использовать простой мьютекс. К счастью, "критический" участок скрипта, который должен быть взаимно исключен, на самом деле не является критическим (скорее для экономии ресурсов).
-
@morpheus93, простой пример: mutex.xml
-
@sergerdn, если между получением и присвоением глобальной переменной нет асинхронного кода, то это будет работать как нужно, первый поток получивший значение присвоит переменной своё значение, а остальные потоки получат значение установленное первым потоком, потому что работа с глобальными переменными синхронизирована между потоками
-
@GhostZ said in Запуск приложения в нескольких потоках, но вызов одной функции только в одном потоке:
@sergerdn, если между получением и присвоением глобальной переменной нет асинхронного кода, то это будет работать как нужно, первый поток получивший значение присвоит переменной своё значение, а остальные потоки получат значение установленное первым потоком, потому что работа с глобальными переменными синхронизирована между потоками
Нельзя в одну атомарную команду поместить и проверку значения глобальной переменной и запись в нее.
- поток_1 проверил условие, должен перейти к присвоению значения глобальной переменной, в это время передалось управление поток_2
- поток_2 проверил значение глобальной переменной и тоже переходит к присвоению ей значения
- тут неважен порядок дальнейшего выполнений кода. А важно, что оба потока последовательно присваивают значение переменной и код в каждом потока считает, что захватил монопольное управление логикой и продолжает дальше работать. А должен работать только один поток.
В случае, если глобальные переменные синхронизированы между потоками, то не будет race condition. В чем я и не сомневался, а говорил о другом.
-
@sergerdn, вы просто видимо не знаете как синхронный код BAS работает в многопотоке, ну вот тогда вам пример sync_code_in_thread.xml, думаю по логу станет понятно)
Также выше я приложил пример с нужной автору темы логикой
-
@GhostZ said in Запуск приложения в нескольких потоках, но вызов одной функции только в одном потоке:
... ну вот тогда вам пример sync_code_in_thread.xml, думаю по логу станет понятно)
Офигеть, я считал, что потоки в BAS это параллельное исполнение кода, так как используется термин threads.
А оказывается, у BAS свое понимание параллельности. И, судя по приложенному скрипту,
переключению контекста между потоками происходит после вызова sleep, а не каким то встроенным планировщиком в произвольном порядке. Как обычно бывает в самой Windows, к примеру. Или в пачке языков программирования.Наверное, моя ошибка в том, что я провел параллели в терминах там, где это было не уместно😕
Я замечал ранее, что переключение контекста между потоками происходит по sleep и даже сам использовал этот прием. Вот только неправильно понимал природу его работы.
Спасибо большое за разъяснения, пошел еще раз перечитывать документацию, так как я умудрился пропустить описание такого поведения.
-
@sergerdn, sleep ничего не переключает, просто sleep асинхронная функция, в BAS синхронный код выполняется последовательно, а асинхронный параллельно, sleep используется просто для примера, на его месте может быть любое другое асинхронное действие, работа с браузером, Node.js и т.д
-
@GhostZ said in Запуск приложения в нескольких потоках, но вызов одной функции только в одном потоке:
@sergerdn, sleep ничего не переключает, просто sleep асинхронная функция, в BAS синхронный код выполняется последовательно, а асинхронный параллельно, sleep используется просто для примера, на его месте может быть любое другое асинхронное действие, работа с браузером, Node.js и т.д
Да, я это понимаю. Осталось понять, как узнать какой код синхронный, а какой - нет.
Надеюсь, где-то в документации это написано, просто я невнимательно читал/забыл/пропустил/etc. И мой собственный код, вероятно, работал чудом в каких-то моментах.
Так как я постоянно использую "лапшу" из вставок кода на NodeJS и почти не использую встроенные "кубики", то для меня BAS становится весь асинхронный, как я считал до этого топика.
-
@sergerdn said in Запуск приложения в нескольких потоках, но вызов одной функции только в одном потоке:
Офигеть, я считал, что потоки в BAS это параллельное исполнение кода, так как используется термин threads.
Да ты офигеть как много считаешь не в тему ... но это лично твоя проблема.
Логика такова - хорошо понимаешь логику работы баса (логику построения кубиков) ? - в тему помогаешь пользователям ? - пользователи развиваются ? - платят разработчику ? ... Развивается софт ? - всем хорошо ?...
Или - пишешь много несуразных постов (горе от ума)? - которые мало кто понимает, засераешь и усложняешь пониманимание для новичков? - меньше пользователей, меньше продаж ... сосем все вместе...
Прежде чем написать - подумай - решаешь ли ты конкретно эту задачу, на сколько ты решаешь эту задачу просто в контексте данного софта ... Нужно ли такое решение ? Ты точно понимаешь какую задачу решаешь? и соответствуют ли твои решения данному софту? ...
-
@DrPrime said in Запуск приложения в нескольких потоках, но вызов одной функции только в одном потоке:
Немного не понимаю, почему нельзя синхронизировать всё в OnApplicationStart?
один адекватный ответ ...
-
@mister40mrdoors said in Запуск приложения в нескольких потоках, но вызов одной функции только в одном потоке:
@sergerdn said in Запуск приложения в нескольких потоках, но вызов одной функции только в одном потоке:
Офигеть, я считал, что потоки в BAS это параллельное исполнение кода, так как используется термин threads.
Да ты офигеть как много считаешь не в тему ... но это лично твоя проблема.
Логика такова - хорошо понимаешь логику работы баса (логику построения кубиков) ? - в тему помогаешь пользователям ? - пользователи развиваются ? - платят разработчику ? ... Развивается софт ? - всем хорошо ?...
Или - пишешь много несуразных постов (горе от ума)? - которые мало кто понимает, засераешь и усложняешь пониманимание для новичков? - меньше пользователей, меньше продаж ... сосем все вместе...
Прежде чем написать - подумай - решаешь ли ты конкретно эту задачу, на сколько ты решаешь эту задачу просто в контексте данного софта ... Нужно ли такое решение ? Ты точно понимаешь какую задачу решаешь? и соответствуют ли твои решения данному софту? ...
Спасибо за пост. Обязательно учту ваши пожелания.