Two Scripts V.S. A Coroutine?

Hey! I’m a bit confused about coroutines. Would two different scripts achieve the same thing as one script with a coroutine?

Examples:

Two Scripts:

while true do
    print("hi")
    wait(1)
end
while true do
    print("Bye")
    wait(1)
end

One Script w/ Coroutine:

coroutine.resume(coroutine.create(function()
    while true do
        print("hi")
        wait(1)
    end
end))

while true do
    print("Bye")
    wait(1)
end

Yes and you can achieve a similar effect using BindableEvent to communicate between both scripts.

edit: but there is a small difference, you don’t have to create a new script when using couroutines.

1 Like

Do you know of any sort of difference under the hood? I could be entirely wrong but my initial thought is that each script is run as it’s own coroutine to begin with. As far as parallel lua goes, is that truly using different threads or is that some other odd thing that is happening under the hood as well?

1 Like

coroutine creates a new function environment so no other globals are inherited. The claim that it is efficient is pretty stupid to be honest. It is the opposite. However we still do it because it can be helpful to be running a task in the background

1 Like

according to Roblox: A coroutine is used to perform multiple tasks at the same time from within the same script. Such tasks might include producing values from inputs or performing work on a subroutine when solving a larger problem. A task doesn’t even need to have a defined ending point, but it does need to define particular times at which it will yield (pause) to let other things be worked on.

read more about it there: coroutine (roblox.com)

1 Like

A coroutine is similar to opening a new (synced) thread.
Think of it this way: each script runs on a different thread. Now you want something to happen while the script is running, maybe wait for a screen to load, but if you wait on your main script you’re stopping the thread. With coroutine you can open a new thread - a new “script”, that will run what’s in it only.
The only thing about coroutines is that they are synchronized - this means they depend on one another. Maybe this is good for a car wash, if you have a queue and only one car can go at a time, but it is not so great if there are infinite car washes so there is no need for any of them to wait. That’s why you have Spawn() - for creating async threads where there is no need for dependency.
Hope it makes some sense.

2 Likes

I know many people have already answered your question, but I would like to introduce to you a spawn() function which acts just like coroutines (but does not accept arguments) and is much simple to implement. It is just a good tip to use spawn() instead of coroutine.resume(coroutine.create()) unless if you are planning to put arguments in.

spawn(function()
while wait() do
print("Hi")
end
end)

while wait() do
print("Bye")
end
1 Like

spawn actually has an little delay at the start, so for something that every second is inportant, I won’t reccomend it.

1 Like

Generally, I think you should keep things that have the same purpose in the same script (module script/script/local script). You can start a new thread if you need to.

My opinion is that it is less messy

I rarely ever use BindableEvent if not, never. But you can do whatever you want.

Well, I would argue that using coroutine.wrap(function)() has the same level of simplicity as but better than spawn() or delay().

Maybe I wasn’t super clear. I know what a coroutine is, but I was curious about what is happening under the hood with it, and how it compares to having two scripts. I know coroutines don’t actually use multiple threads, so I was curious how exactly they do what they do. Lua is a single threaded scripting language, but Roblox also introduced Parallel Lua. Does that actually use multiple threads?

So basically lua right is what we call a interpreeted language. It is constantly feeding byte code (think like machine code for a virtual machine). We organize our machine code with the environment. It tells us what globals are inherited and what locals are inherited in the current scope. Coroutines basically create a new environment. See this
Lua 5.2 Tutorial 14: The Environment - YouTube

Green threading. Threads are scheduled to run (for e.g by the VM) on the same hardware thread (so not in a truly parallel manner then) and are swapped whenever you yield, this makes it look like multiple threads are in use. On another note Actor objects can be used for parallel execution but I advise reading the official thread to know more about the implementation.

For extra information Lua is single threaded but the engine is multi-threaded (read more on the second linked thread). Also when calling spawn you need to wait for an open slot in the scheduler since there’s an initial delay, not the same for coroutines.

Edit: just so you know the post ahead must refer to something else in this thread as misinformation because this couldn’t possibly be factually incorrect; this information is verified by the research that was put in. Pity FilteredStudio didn’t bother to address anything to potentially help clarify than lazily point out misinformation.

2 Likes

I’d probably use RunService.Heartbeat or something rather than a while loop. You can bind multiple functions to the same event, meaning you can have an unlimited amount of loops within one script.

There’s a lot of misinformation being spread here so I’ll summarise coroutines as much as I can.

Coroutines allow multiple threads to run under the same script environment, but unlike spawn, there is no delay. They’re almost identical to events.

There’s a few notes about coroutines that should be addressed before I continue, when you start a coroutine with yields, the coroutine block will run before continuing, this is to mitigate race conditions.

local co = coroutine.wrap(function()
  while true do --ik this is ugly but i'm providing an example
    print("ran inside the coroutine")
    wait()
  end
end)

co()
print("ran outside the coroutine")
ran inside the coroutine
ran outside the coroutine
ran inside the coroutine
...

Scripts give you a race condition, however if you use coroutines with the information I’ve just provided, it helps mitigate such a thing from happening.

1 Like