Функции для многопоточного скриптинга
Posted: 2010-02-01 18:36:10
Встречаются шарды на которых стоит минимальная задержка между всеми действиями. На таких шардах очень трудно сделать нормальный многопоточный скрипт, особенно если каждая часть скрипта должна иногда что-то выполнять.
К примеру приведу шард на котором я сейчас играю - UOSecondAge. Одна из "особенностей" шарда - можно одновременно использовать сразу несколько навыков, главное между их использованием должна быть задержка минимум 500мсек.
Именно для этой цели я написал несколько функций реазизующих подобие мьютексов.
Стоит заметить, функции захламляют реестр и никак не очищают. Для каждого actionType используются 2 ключа в реестре для каждого клиента.
Для любопытных, задающихся вопросом "почему не использовать GetGlobal/SetGlobal" - отвечаю: они не синхронизированы. Тоесть если два потока попытаются считать одновременно один и тот-же GetGlobal - клиент зависнет. Поэтому пришлось через задницу использовать SetEasyUO/GetEasyUO.
Опишу на примере, зачем они нужны:
Если запустить обе функции одновременно - они будут только друг другу мешать. Т.к. часто будет запускаться chopTree() и createBow() с интервалом менее чем 500мсек и один из них будет сбиваться.
Теперь вариант с моими функциями:
Теперь, каждая функция будет ждать свободного мьютекса. Как только он освободится - она его займёт на указанное количество милисекунд. Только через указанное количество секунд - вторая функция сможет занять мьютекс и выполнить свои действия. Теперь функции не будут друг другу мешать, а будут выполнять свои действия соблюдая дистанцию в 500мсек.
Не думаю что есть много шардов где это можно использовать, и скриптеров которые это сумеют использовать, но всётаки выложу.
Проверочный скрипт (для проверки на работоспособность):
Ну и сами мьютексы:
Первый параметр у функции - ID мьютекса. Это должно быть обязательно число. Поэтому сверху файла можете обьявить пару констант (см. пример выше).
П.С. Теперь мой чар ходит по лесу как комбайн. Одновременно рубит лес, перемалывает его в луки и табуретки
П.П.С. Если здесь есть кто понял что это и зачем оно нужно - отпишитесь
К примеру приведу шард на котором я сейчас играю - UOSecondAge. Одна из "особенностей" шарда - можно одновременно использовать сразу несколько навыков, главное между их использованием должна быть задержка минимум 500мсек.
Именно для этой цели я написал несколько функций реазизующих подобие мьютексов.
Стоит заметить, функции захламляют реестр и никак не очищают. Для каждого actionType используются 2 ключа в реестре для каждого клиента.
Для любопытных, задающихся вопросом "почему не использовать GetGlobal/SetGlobal" - отвечаю: они не синхронизированы. Тоесть если два потока попытаются считать одновременно один и тот-же GetGlobal - клиент зависнет. Поэтому пришлось через задницу использовать SetEasyUO/GetEasyUO.
Опишу на примере, зачем они нужны:
Code: Select all
sub lumberjack()
while true
getNextTree()
chopTree()
wend
endsub
sub bowcraft()
while true
getPlanks(7)
createBow()
wend
endsub
Если запустить обе функции одновременно - они будут только друг другу мешать. Т.к. часто будет запускаться chopTree() и createBow() с интервалом менее чем 500мсек и один из них будет сбиваться.
Теперь вариант с моими функциями:
Code: Select all
var timerAction = 0
sub lumberjack()
while true
getNextTree()
registerAction(timerAction,500)
chopTree()
wend
endsub
sub bowcraft()
while true
getPlanks(7)
registerAction(timerAction,500)
createBow()
wend
endsub
Теперь, каждая функция будет ждать свободного мьютекса. Как только он освободится - она его займёт на указанное количество милисекунд. Только через указанное количество секунд - вторая функция сможет занять мьютекс и выполнить свои действия. Теперь функции не будут друг другу мешать, а будут выполнять свои действия соблюдая дистанцию в 500мсек.
Не думаю что есть много шардов где это можно использовать, и скриптеров которые это сумеют использовать, но всётаки выложу.
Проверочный скрипт (для проверки на работоспособность):
Code: Select all
sub startThreads()
var i
for i=0 to 5
UO.Exec('exec testRegisterAction')
next
endsub
Sub testRegisterAction()
var id = UO.Random(10000)
while true
registerAction(0,500)
UO.Print("Hello from thread "+str(id))
wend
endsub
Ну и сами мьютексы:
Code: Select all
### REGISTER ACTION FUNCTION
Sub registerAction(id,time)
var lockID = str(id) + str(UO.Random(1024 * 1024))
var lockVarId = id*2+0
var lockEndsId = id*2+1
var oldLockID
var waitTime = 0
while true
oldLockID = getInstanceVar( lockVarId )
#If this action is already locked, wait till it gets unlocked
repeat
wait(50)
until (val( getInstanceVar( lockEndsId ) ) <= uo.Timer())
#If time went up, but action is owned by same lockid, we can unlock it
if ( getInstanceVar( lockVarId ) == oldLockID ) then
#And relock to ourselves
setInstanceVar( lockVarId , lockID)
#Setting timer is required in case some new thread comes to the beginning of this whole function and bypass all the checks
setInstanceVar( lockEndsId , str(UO.Timer() + 1) )
#Now we wait for some time, and check if we are still the owners of this lock. If several processes come to this spot - only one will be alive at the end
wait(50) #!!!WARNING IF YOU HAVE A SLOW COMPUTER OR MANY UO INSTANCES - SET THIS TO HIGHER VALUE
if ( getInstanceVar( lockVarId ) == lockID ) then
#Now set our normal timer
setInstanceVar( lockEndsId , str(UO.Timer() + (time/100)) )
return lockID
endif
endif
#It could take some time for other thread to initialize it's mutex, so lets wait a little
wait(100)
wend
endsub
Sub unregisterAction(id)
var lockEndsId = id*2+1
setInstanceVar( lockEndsId, str(0))
endsub
Sub reregisterAction(id, time)
var lockEndsId = id*2+1
setInstanceVar( lockEndsId, str(UO.Timer() + (time/100)))
endsub
Sub isRegisteredAction(id)
var lockEndsId = id*2+1
if (val( getInstanceVar( lockEndsId ) ) > uo.Timer() ) then
return true
else
return false
endif
endsub
### INSTANCE HANDLING
Sub getInstanceKey()
return UO.Hex2Int(UO.GetSerial())
endsub
Sub setInstanceVar(id,value)
UO.SetEasyUO(getInstanceKey()*1000+id,value)
endsub
Sub getInstanceVar(id)
return UO.GetEasyUO(getInstanceKey()*1000+id)
endsub
Первый параметр у функции - ID мьютекса. Это должно быть обязательно число. Поэтому сверху файла можете обьявить пару констант (см. пример выше).
П.С. Теперь мой чар ходит по лесу как комбайн. Одновременно рубит лес, перемалывает его в луки и табуретки

П.П.С. Если здесь есть кто понял что это и зачем оно нужно - отпишитесь
