Threading in FiveM
-- random block of stuff so there's a space between the title and the thread!
I haven’t seen a proper guide on multi-threading, and there’s a lot of myths and misconceptions about what it actually is; read this and you’ll be a smarter scum of the earth.
I am writing this tutorial assuming you have surpassed ‘basic beta male server owner’
This means, you have done the following;
- Properly set up your FXServer
- Have at least once, successfully operated a text editor and opened a lua file
- Stopped begging for help with ESX errors in the #fxserver-support channel.
This being said, I am going to use a few terms which you may not yet be familiar with, so here they are;
- Thread : a split process operation that can run simultaneously with the main program
- Synchronous : code that runs in order of writing
- Asynchronous : code that runs instantaneously, with no delays for previous tasks
- Multi-threading : running multiple threads of execution concurrently
And a few terms I won’t use for fear of hurting brains, but you should research if you ever care to advance further towards becoming an ‘assembly god chad’ and less of a 'python script kevin’
- TLB’s : Translation Lookaside Buffers (the reason multithreading, or single thread operations can go to shit)
- Multiprocess Thread Joining : the process of joining threads to new processes so as not to interupt the creation of new threads within a process
- Thread Limitations : the physical hardware and software limitations that require thread termination (bad, don’t do that) or thread joining (good, do that) with the main process/subprocesses
Finally; before we get into things, a few myths about threading debunked;
- Threads are Asynchronous : not true, a thread can run parallel with it’s parent script but the contents of a thread run synchronously.
- Threads make fxserver go boom : again, not true - use them intelligently and you’ll find them quite useful.
- wHy NoT PuT EvErY liNe In A ThReAd : http://sites.rootsweb.com/~nyononda/hospitals/idiotsasylum.html
What is threading in FiveM?
Threads in FiveM allow us to run operations within code instantaneously, without affecting the primary operation of the code. This is useful if you have an operation that needs to run that could take a while to complete, but you do not want to interrupt the rest of the script. For instance; iterating through a LARGE amount of data. It can also be used within a loop in order to very quickly complete an indexing operation, but more on that later.
In order to create a basic thread;
Citizen.CreateThread(function()
-- code to be executed on a separate thread
end)
This will spawn a thread separate of the primary process. I will continue to refer to each .lua file as the ‘primary process’
How Threads Work
Threads allow you to execute code that would otherwise halt the entire operation at the same time that the parent code in the main process continues to execute. In more advanced configurations, it allows you to complete slow and laborious tasks extremely quickly.
Examine the following code;
print("First Print")
Wait(1000)
print("Second Print")
In this example, the console will output “First Print” and then one second later it will output “Second Print”
However, using threading;
-- First Thread
Citizen.CreateThread(function()
print("First Print")
Wait(1000)
print("Second Print")
end)
-- Second Thread
Citizen.CreateThread(function()
Print("Third Print")
end)
In this example, the console output will look like this
“First Print”
“Third Print”
(one second delay)
“Second Print”
So how’d that happen? Because we’re using threads, the code executed without acknowledging the Wait within the first thread, and continued to spawn the second thread instantaneously. The Wait was only acknowledged within the context of the first thread, and the operations within.
Hypothetical Usage
Okay, so let’s make this useful.
I have the following code; (note - this is hypothetical, sloppy, and I don’t see why you would do this but, it’s a good example )
-- assume a table with a true/false entry for each player on the server
-- when a player types /confirm their entry is changed to true
for i=1,32 do
if table[i] then
while table[i].status == false
Wait(0)
end
print("Player has used /confirm")
end
end
The problem with this code, is that it will hang up on the first valid table until /confirm is typed by that player; we can solve this problem by threading;
-- assume a table with a true/false entry for each player on the server
-- when a player types /confirm their entry is changed to true
for i=1,32 do
if table[i] then
Citizen.CreateThread(function()
while table[i].status == false
Wait(0)
end
print("Player has used /confirm")
end)
end
end
This will spawn a thread for every VALID entry, and allow the loop to run all the way to 32.
Loops In Threads
Just because your thread is separate from the main process, does not mean it cannot cause issues for the parent. For instance, creating a while loop with no wait will still crash your clients or server.
-- this is an example of improper loop usage within a thread
Citizen.CreateThread(function()
while true do
print("y does essentialmode databus not work 4 me ?? ? ? ? !!!")
end
end)
Also; never, ever, ever, ever, ever, ever, ever, do this -
while true do
Citizen.CreateThread(function()
print("hello im here to overload your thread limitz")
end)
end
Spawning threads within a while loop with no foreseeable end is a terrible idea. I am unsure of the ‘thread limit’ within FiveM, however I do know that creating a million threads will cause a bunch of bad stuff to happen.
Understanding Thread-based Problems
The use of threads in any language can generate problems, and unexpected results should you not understand them fully. I’m going to go over what I believe to be the most common issues that come from the use of threads;
The problem with multithreading, is that it does not alleviate the resource usage that would come from doing each operation without a thread. For instance, in python, let’s use the following hypothetical.
In python, my code has pulled json information from a web-server and parsed that information into table structure.
I now want to iterate through that table structure for every field named ‘itemname’ and there are over 10,000 items, and I then want to write that item name to a file.
Without threading, it would look like this ( not python syntax for understanding purposes )
GET <url>
<PARSE JSON TO VARIABLE 'ITEMS'>
<LOOP THROUGH EACH ITEM AND WRITE ITEMNAME TO ITEMNAMES.TXT>
There would be an operation delay within the loop, as it would wait for the write operation to finish before it continues on to iteration N + 1. However, if we were to insert a thread before the write portion of that loop, we would be attempting to write 10,000 items to a file all at once, as well as allowing that loop to run away until it reached item 10,000. This would require a significant amount of processor usage and disk usage in order to complete, not to mention hitting the thread limit for python ( which is hardware based, in my case roughly 950 threads ).
This is why when creating a thread in FiveM, especially within a loop, you have to acknowledge the potential resource usage that will come from the operation you are performing. Implement proper process sleep timers between the creation of threads within a loop, and acknowledge the total thread count that will come from your application.
Another thread based issue results from a lack of understanding of the bridging between synchronous execution and asynchronous execution when implementing threads inside of a synchronous block of code - as displayed in our print example.
Citizen.CreateThread(function()
Wait(3500)
print("one")
end)
Wait(2500)
Citizen.CreateThread(function()
Wait(100)
print("two")
end)
print("three")
In the above example, the output will look like this;
“three”
“two”
“one”
This is because the threads run separately from the main process. In our first thread, we wait 3500ms, which is longer than the 2500ms wait in the main process. The second thread waits 100ms, but the final print of “three” will execute at the same time as the new thread being spawned, which is told to wait 100ms before printing “two.”
This was brain to keyboard; hope you find it useful.
stuff and things
tl;dr threadz are cool but can make your pc go kaboom
i didn’t proofread any of this so half of it probably just sounds like ramble
yes i’m aware citizen.createthread() is actually just a function registering a coroutine and calling it, go away.
i swear to god if i see one more resource with an entire .lua file encased in Citizen.CreateThread i’m putting your dog on wikileaks