Page 1 of 1

Potential alternative to threads for some cases

Posted: Fri Aug 01, 2008 3:19 pm
by dracflamloc
This is a bit of code I have used in the past mainly in games and back when pb wasn't threadsafe, however I still find it useful in certain scenarios and figured i'd share.

Code: Select all

Structure TASK_INFO
  function.l
  lastTimeMs.l
  delayMs.l
  autoremove.b
  parameter.l
  useParam.b
EndStructure

Global NewList TaskList.TASK_INFO()

Procedure RegisterTask(*func,delay.l,autoremove.b)
  AddElement(TaskList())
  TaskList()\function=*func
  TaskList()\delayms=delay
  TaskList()\lastTimeMs=ElapsedMilliseconds()-1
  TaskList()\autoremove=autoremove
EndProcedure

Procedure RegisterTaskEx(*func,delay.l,autoremove.b,parameter.l)
  AddElement(TaskList())
  TaskList()\function=*func
  TaskList()\delayms=delay
  TaskList()\lastTimeMs=ElapsedMilliseconds()-1
  TaskList()\parameter=parameter
  TaskList()\useparam=1
  TaskList()\autoremove=autoremove
EndProcedure

Procedure RunTasks(nowMs.l)
  ForEach TaskList()
    If nowMs - TaskList()\lastTimeMs >= TaskList()\delayMs
      If TaskList()\UseParam
        CallFunctionFast(TaskList()\function,TaskList()\parameter)
      Else
        CallFunctionFast(TaskList()\function)
      EndIf 
      TaskList()\lastTimeMs=nowMs
    EndIf 
  Next 
  
  ForEach TaskList()
    If TaskList()\autoremove And TaskList()\lastTimeMs=nowMs
      DeleteElement(TaskList())
    EndIf 
  Next 
EndProcedure

Posted: Fri Aug 01, 2008 5:06 pm
by Hroudtwolf
Sorry, but I can't see the point of ..."Potential alternative to threads for some cases".
There is no alternative. It is totaly another way.

Regards

Wolf

Posted: Fri Aug 01, 2008 6:46 pm
by superadnim
this is like running a convection motor inside another convection motor... it just doesn't make much sense does it. threads can be paralleled and the OS can assign each one to a different core. now that makes sense and that is the point of threads too.

even if branch prediction does its job and you don't have any bubbles I think you are wasting lots of precious cycles by doing what you showed us.

i understood the reason of why you decided to take actions when no threadsafe was available, but it just doesn't make much sense to me either, unless you add error handling and some other debugging tools to this, it doesn't look like someone would use it in a real-world situation.

however the snippet is useful in the sense that it shows others how to perform scheduled tasks within the process, and that is useful indeed.

but threads alternative, not by any chance. :)

Posted: Fri Aug 01, 2008 6:48 pm
by SFSxOI
What are some of these "certain scenarios" when this would be used?

Posted: Fri Aug 01, 2008 6:56 pm
by superadnim
in my way of reasoning this is useful, in games, when you handle scheduled tasks such as pinging clients or cleaning up the map (although it depends a lot on your design).

example:

Code: Select all

;- scheduled tasks example

Procedure.l Task_Ping()
	PrintN("pinging has been done")
EndProcedure
Procedure.l Task_Clean()
	PrintN("clean has been done")
EndProcedure

If OpenConsole()
	
	RegisterTask( @Task_Ping(), 1000, #False )
	RegisterTask( @Task_Clean(), 3000, #False )
	
	Repeat
		RunTasks( ElapsedMilliseconds() )
		Delay(100)
	Until Inkey() = Chr(27)
	
	CloseConsole()
EndIf
for the record thanks for sharing your code, i wasn't bashing you or anyone with my previous comment :)

Posted: Fri Aug 01, 2008 7:21 pm
by dracflamloc
That is actually some of what I am using it for. And you really aren't wasting many cycles unless you've got one heck of a long list of 'tasks' to go through. Threads themselves aren't totally without cost, and mutex locking can nullify many of the benefits of forking into threads in the first place.

Hrou:
"It is totally another way." is essentially a definition for the word 'alternative'.

Obviously this *is* a blocking method so it cannot run parallel to tasks, but as was mentioned above, this can be used for code which will not take an excessively long time, but doesn't need to be run every game loop. I did not say it was an equivalent of threads, only an alternative, per the definition of the word.

For example maybe you only want AI to tick at a rate of about 15 fps, regardless of a faster draw framerate. This could work well in such a scenario especially if you're dealing with arrays in structures and attempting to access them in a threadsafe manner, etc.

The majority of games are single-threaded for everything, except recently they have started breaking physics into separate threads for cutting-edge games. I can pretty much guarantee nobody is creating a cutting-edge game in PB.

Anyway, if you find this useful, great. If you don't... why are you bothering to read this 'tip' in the first place?

Posted: Fri Aug 01, 2008 11:40 pm
by superadnim
Actually, now that you mention AI I got to think, this allows you to run much more complex AIs while keeping clean and safe code, which can't be bad.

As for other applications, anything that would require a timed / delayed execution line could benefit from an abstracted scheduler layer.

The cool thing would be to run the scheduler loop on a separate thread, that way we get the best out of both worlds.

Posted: Sat Aug 02, 2008 3:18 pm
by superadnim
One thing that occurred to me was that, if taking my previous example, for instance our Task_Clean routine takes 2 seconds to return (it might have a lot of things to do, you never know...) what happens then?, all the other tasks are delayed by 2 seconds!. how to deal with this?

Posted: Sat Aug 02, 2008 4:16 pm
by Godai
This is a generalized state machine, and it's very usefull in game programming. You usually don't want too many threads monkeying about ;)
Most game libraries can have a hard time dealing with thread issues (Ogre for instance) so the good old way of having a generalized state machine is very much used in todays game programming.

Posted: Sun Aug 03, 2008 6:29 am
by dracflamloc
superadnim wrote:One thing that occurred to me was that, if taking my previous example, for instance our Task_Clean routine takes 2 seconds to return (it might have a lot of things to do, you never know...) what happens then?, all the other tasks are delayed by 2 seconds!. how to deal with this?
A potential solution would be to split the problem into chunks that run at shorter intervals, or have timed callbacks within the task to do the main processing that needs to be done mid-task.

Posted: Sun Aug 03, 2008 2:00 pm
by superadnim
Godai, are you sure State Machine is the correct term?, and if so, why?. It's nothing like the state machines I've seen before, this just runs operations on a timed basis, whereas state machines have, well, states to conform with.

But I am eager to learn more about your definition since I might be wrong.

dracflamloc, I am not sure how to go around this || how to implement it. But one thing that could be done is a time compensation, if a task takes longer than the next interval then you could compensate the time for the next task?. perhaps with a prediction loop this could be compensated very well to a certain extent.

Posted: Sun Aug 03, 2008 5:42 pm
by dracflamloc
Well usually a time-intensive piece of code is able to be split into smaller sub-problems.

Take looping through an array for example:

For i=0 to 100000
abc(i) = i
Next

This could be split into groups of 1000:

startgroup=1000*group
For i = startgroup to startgroup+1000
abc(i) = i
Next

The 'task' could be run at smaller intervals and only do 1000 indices per run