Potential alternative to threads for some cases

Share your advanced PureBasic knowledge/code with the community.
dracflamloc
Addict
Addict
Posts: 1648
Joined: Mon Sep 20, 2004 3:52 pm
Contact:

Potential alternative to threads for some cases

Post 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
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

Post 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
superadnim
Enthusiast
Enthusiast
Posts: 480
Joined: Thu Jul 27, 2006 4:06 am

Post 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. :)

:lol: should I bash the keyboard and give up?
:?
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Post by SFSxOI »

What are some of these "certain scenarios" when this would be used?
superadnim
Enthusiast
Enthusiast
Posts: 480
Joined: Thu Jul 27, 2006 4:06 am

Post 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 :)

:lol: should I bash the keyboard and give up?
:?
dracflamloc
Addict
Addict
Posts: 1648
Joined: Mon Sep 20, 2004 3:52 pm
Contact:

Post 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?
superadnim
Enthusiast
Enthusiast
Posts: 480
Joined: Thu Jul 27, 2006 4:06 am

Post 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.

:lol: should I bash the keyboard and give up?
:?
superadnim
Enthusiast
Enthusiast
Posts: 480
Joined: Thu Jul 27, 2006 4:06 am

Post 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?

:lol: should I bash the keyboard and give up?
:?
Godai
Enthusiast
Enthusiast
Posts: 171
Joined: Thu Oct 05, 2006 8:13 pm

Post 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.
dracflamloc
Addict
Addict
Posts: 1648
Joined: Mon Sep 20, 2004 3:52 pm
Contact:

Post 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.
superadnim
Enthusiast
Enthusiast
Posts: 480
Joined: Thu Jul 27, 2006 4:06 am

Post 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.

:lol: should I bash the keyboard and give up?
:?
dracflamloc
Addict
Addict
Posts: 1648
Joined: Mon Sep 20, 2004 3:52 pm
Contact:

Post 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
Post Reply