Posted by
| Nick Gammon
Australia (23,133 posts) Bio
Forum Administrator |
Message
| Lua has a powerful support for something called coroutines, which is a form of threading.
Many times people have asked for a way to make a script that does something, waits for a while, and then keeps going. Now it is possible! Read on ...
As a demonstration, I will make three functions in a script file. The three functions are:
- The coroutine itself (the thread) - this will execute over 10 seconds, printing a number that increments, and yields execution back to MUSHclient in between each one.
- A trigger script that (when fired) starts the thread running
- A "tickler" timer - this runs every second, and gives execution back to the thread
There are other ways you could do this, for example rather than executing the thread every second, you might do it in response to a trigger or alias, or some other event.
The important thing is that the coroutine (thread) keeps its internal status while it is running. However it has to explicitly "yield" when it is ready to pause. This is called "co-operative multitasking".
First function, the thread itself, which is pretty simple:
function backgrounder ()
-- loop doing something, yield execution every time through
for i = 1, 10 do
print ("background thread - step", i)
coroutine.yield ()
end -- for
end -- backgrounder
What we expect to see here is the step number being displayed each time through.
Next part is a "tickler" that will resume this task every second:
function tickle_it ()
-- if background task exists, and is suspended, resume it
if background_task ~= nil and
coroutine.status (background_task) == "suspended" then
coroutine.resume (background_task)
else
EnableTimer ("tickler", false) -- don't need timer now
end -- if
end -- tickle_it
This is called by a timer that keeps firing. The timer is named "tickler", and this function will disable the timer when it isn't needed any more. This would happen when our thread terminates, so its status will go from "suspended" to "dead".
Finally, the trigger to kick the whole process off:
function mytrigger ()
-- see if backgrounder running
if background_task == nil or
coroutine.status (background_task) == "dead" then
-- not running, create it
background_task = coroutine.create (backgrounder)
-- make timer to keep it going
AddTimer ("tickler", 0, 0, 1, "",
timer_flag.Enabled + timer_flag.Replace,
"tickle_it")
end -- if
end -- mytrigger
In the example above I check to see if the background task has a "dead" status, or is nil. If it is nil then we have never created it, if it has a dead status then we had one previously but it terminated.
In either case, we create a new task (with coroutine.create) passing as an argument the function we wish to run (backgrounder). This is stored in the global vairable background_task.
Then we make a timer to call the "tickle_it" routine every second.
Now if you put all three functions into your script file, and then make a trigger to start things going, or just type into the command window:
/mytrigger ()
Then you will see the following lines displayed over a 10-second interval:
background thread - step 1
background thread - step 2
background thread - step 3
background thread - step 4
background thread - step 5
background thread - step 6
background thread - step 7
background thread - step 8
background thread - step 9
background thread - step 10
Incredible, but true! Of course, while this is happening you can do other things (eg. type "look"), because MUSHclient isn't "hanging" in the script.
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|