Posted 18 March 2013 - 10:06 AM
Most people has run into this problem when creating infinite (or really long) loops. And by now, almost everyone knows how to avoid it, the simple "sleep(0)".
But, is that the best way? Well, that depends on the situation, but in most cases the answer is no.
So, what other ways are there?
1- os.pullEvent
In some cases, you don't really need to keep looping, but just wait for an specific event, that's where os.pullEvent is used.
Example:
Imagine you need a program that does something every time there's a redstone signal on it's left. A simple way to do this would be:
Now, we don't need to check all the time, just when there's a change. So, a better way would be:
2- os.queueEvent + os.pullEvent
This one is usefull if you don't need to wait for an specific event, or you need the loop to keep running.
It's almost the same as using sleep(0), since it will queue an event and pulls it. The difference is that this won't use a timer. Now, you may think, if I call sleep(0), the timer will wait 0 seconds until it fires. Well, it won't. Timers actually have a minimum time, which is a tick (or 0.05 seconds). This is not a really big difference, but it can be for really long loops.
So, here's a function that would queue a fake event, and yield until it pulls it:
Now, you just have to use it in a long loop that takes a long time:
So, how can we make this faster? Well, we can't make it resume faster, but we can make it yield only when needed.
Lets make a better yielding function:
Now your big loops won't generate lag on other computers and maybe will be even faster.
Hope this helps someone. Any feedback and comments are welcome.
But, is that the best way? Well, that depends on the situation, but in most cases the answer is no.
So, what other ways are there?
1- os.pullEvent
In some cases, you don't really need to keep looping, but just wait for an specific event, that's where os.pullEvent is used.
Example:
Imagine you need a program that does something every time there's a redstone signal on it's left. A simple way to do this would be:
while true do
if rs.getInput("left") then
-- do something here
end
sleep(0)
end
But this would just make the computer check, the yield to avoid an error, and check again, no matter if there was a change or not.Now, we don't need to check all the time, just when there's a change. So, a better way would be:
while true do
if rs.getInput("left") then
-- do something here
end
os.pullEvent("redstone") -- wait for a change in redstone
end
2- os.queueEvent + os.pullEvent
This one is usefull if you don't need to wait for an specific event, or you need the loop to keep running.
It's almost the same as using sleep(0), since it will queue an event and pulls it. The difference is that this won't use a timer. Now, you may think, if I call sleep(0), the timer will wait 0 seconds until it fires. Well, it won't. Timers actually have a minimum time, which is a tick (or 0.05 seconds). This is not a really big difference, but it can be for really long loops.
So, here's a function that would queue a fake event, and yield until it pulls it:
local function yield()
os.queueEvent("someFakeEventName") -- queue the event
os.pullEvent("someFakeEventName") -- pull it
end
It will resume almost instantly, depending on how many computers are running.Now, you just have to use it in a long loop that takes a long time:
for i = 1, someReallyBigNumber do
doSomeCalculations(someTable[i])
yield()
end
And it will avoid a "too long without yielding". But this can be really slow, specially if there's more computers running.So, how can we make this faster? Well, we can't make it resume faster, but we can make it yield only when needed.
Lets make a better yielding function:
local yieldTime -- variable to store the time of the last yield
local function yield()
if yieldTime then -- check if it already yielded
if os.clock() - yieldTime > 2 then -- if it were more than 2 seconds since the last yield
os.queueEvent("someFakeEvent") -- queue the event
os.pullEvent("someFakeEvent") -- pull it
yieldTime = nil -- reset the counter
end
else
yieldTime = os.clock() -- store the time
end
end
There are other ways to do it, but this should work.Now your big loops won't generate lag on other computers and maybe will be even faster.
Hope this helps someone. Any feedback and comments are welcome.