Yoko

All sides of Injection
It is currently 2025-11-22 19:45:07

All times are UTC+02:00




Post new topic  Reply to topic  [ 5 posts ] 
Author Message
PostPosted: 2010-02-01 18:36:10 
Offline
Expert!
User avatar

Joined: 2005-04-23 10:19:43
Posts: 388
Встречаются шарды на которых стоит минимальная задержка между всеми действиями. На таких шардах очень трудно сделать нормальный многопоточный скрипт, особенно если каждая часть скрипта должна иногда что-то выполнять.
К примеру приведу шард на котором я сейчас играю - UOSecondAge. Одна из "особенностей" шарда - можно одновременно использовать сразу несколько навыков, главное между их использованием должна быть задержка минимум 500мсек.
Именно для этой цели я написал несколько функций реазизующих подобие мьютексов.

Стоит заметить, функции захламляют реестр и никак не очищают. Для каждого actionType используются 2 ключа в реестре для каждого клиента.
Для любопытных, задающихся вопросом "почему не использовать GetGlobal/SetGlobal" - отвечаю: они не синхронизированы. Тоесть если два потока попытаются считать одновременно один и тот-же GetGlobal - клиент зависнет. Поэтому пришлось через задницу использовать SetEasyUO/GetEasyUO.

Опишу на примере, зачем они нужны:

Code:
sub lumberjack()
   while true
      getNextTree()
      chopTree()
   wend
endsub

sub bowcraft()
   while true
      getPlanks(7)
      createBow()
   wend
endsub


Если запустить обе функции одновременно - они будут только друг другу мешать. Т.к. часто будет запускаться chopTree() и createBow() с интервалом менее чем 500мсек и один из них будет сбиваться.

Теперь вариант с моими функциями:
Code:
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:
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:
### 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 мьютекса. Это должно быть обязательно число. Поэтому сверху файла можете обьявить пару констант (см. пример выше).

П.С. Теперь мой чар ходит по лесу как комбайн. Одновременно рубит лес, перемалывает его в луки и табуретки :D

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


Last edited by Beyonder on 2010-02-01 22:16:43, edited 3 times in total.

Top
   
PostPosted: 2010-02-01 20:00:01 
Offline
User avatar

Joined: 2009-05-28 09:58:28
Posts: 2802
Location: Иваново
Как я понял ты вводишь InstanceVar только для записи в реест? Почему него не очистить по завершению удалив и пересоздав соответствующую ветку?
Ведь потом сам же сократишь раза в 2 :)

_________________
Image
YokoInjection CodeSweeper
Ошибка "Unhandled exception in parser"
Стрелялка для олдов.


Top
   
PostPosted: 2010-02-01 20:33:24 
Offline
Expert!
User avatar

Joined: 2005-04-23 10:19:43
Posts: 388
Обновил скрипт, по глупости использовал GetGlobal и SetGlobal (которые не синхронизированы) в синхронизированной функции setInstanceVar и getInstanceVar - из-за чего их смысл потерялся и скрипт безбожно глючил. Теперь привязал всё к серийнику персонажа, так даже лучше.

Quote:
Как я понял ты вводишь InstanceVar только для записи в реест?


InstanceVar - Основное назначение функции это запись и чтение переменных в пределах текущего УО в синхронизированном виде. Тоесть чтобы быть уверенным что одну и ту-же переменную в один момент времени могут без проблем прочитать два потока. Реализована она через реестр, т.к. других синхронизированных переменных я не нашёл.
Был вариант сделать через UO.AddObject (там тоже синхронизировано), но тогда появляются тонны спама в клиенте.

Quote:
Почему него не очистить по завершению удалив и пересоздав соответствующую ветку?

Покажи мне функцию удаления из реестра, я вижу только setEasyUO и getEasyUO :)


Top
   
PostPosted: 2010-02-01 21:51:58 
Offline
User avatar

Joined: 2009-05-28 09:58:28
Posts: 2802
Location: Иваново
Beyonder wrote:
Покажи мне функцию удаления из реестра, я вижу только setEasyUO и getEasyUO :)

задействовать внешний файл с командами reg delete/reg add :roll: Топором зато работает ;)

_________________
Image
YokoInjection CodeSweeper
Ошибка "Unhandled exception in parser"
Стрелялка для олдов.


Top
   
PostPosted: 2010-02-01 22:08:39 
Offline
Expert!
User avatar

Joined: 2005-04-23 10:19:43
Posts: 388
Ну это уже совсем изврат... Текущая версия впринципе почти не замусоривает реестр. Она использует по 2 ключа на каждого персонажа (на каждый уникальный серийник), так что даже если у нас 10 персонажей, то будет каких-то 20 ключей в реестре...


Top
   
Display posts from previous:  Sort by  
Post new topic  Reply to topic  [ 5 posts ] 

All times are UTC+02:00


Who is online

Users browsing this forum: No registered users and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Limited