FiveM CreateThread คืออะไร? ใช้อย่างไรไม่ให้ Script กิน CPU
FiveM CreateThread() คือ Function ของ CfxLua สำหรับสร้าง Thread หรือ Coroutine ใหม่ เพื่อให้ Code ชุดหนึ่งสามารถทำงานแยกจาก Flow หลักของ Script ได้ โดยพบได้บ่อยมากในระบบที่ต้องทำงานต่อเนื่อง เช่น ตรวจตำแหน่ง Player, ตรวจปุ่ม, วาด Marker, รอโหลด Model หรือทำงานเป็นช่วงเวลา
ตัวอย่างพื้นฐานคือ
CreateThread(function()
print('Thread started')
end)
หรือเขียนชื่อเต็ม
Citizen.CreateThread(function()
print('Thread started')
end)
ทั้งสองรูปแบบพบได้ใน FiveM Lua
แต่จุดสำคัญที่สุดที่ต้องจำคือ หากใช้ while true do ภายใน Thread ต้องมี Wait() ในเส้นทางการทำงานของ Loop ไม่เช่นนั้น Client อาจค้างหรือเกมอาจ Crash ได้
ตัวอย่างที่ถูกต้องคือ
CreateThread(function()
while true do
Wait(1000)
print('Running every second')
end
end)
บทความนี้จะอธิบาย CreateThread() ตั้งแต่พื้นฐานไปจนถึงการใช้ร่วมกับ Wait(0), Native, Marker, Controls, Adaptive Sleep และการ Optimize Resource
① FiveM CreateThread คืออะไร
CreateThread() ใช้สร้าง Thread ใหม่ใน CfxLua Runtime
Syntax หลักคือ
CreateThread(function()
-- code
end)
ชื่อเต็มคือ
Citizen.CreateThread(function()
-- code
end)
เอกสาร Cfx.re ระบุว่า Code ภายใน Handler ของ CreateThread() จะถูก Execute แบบ Asynchronous จาก Flow ที่สร้าง Thread นั้น
ตัวอย่าง
print('A')
CreateThread(function()
print('B')
end)
print('C')
แนวคิดคือ Script ไม่จำเป็นต้องหยุดรอให้ Thread จบก่อนจึงทำ Code หลัง CreateThread() ต่อ
② CreateThread ใช้ทำอะไร
Use Case ที่พบได้บ่อย เช่น
ตรวจ Player Position
ตรวจ Player State
ตรวจ Keyboard/Controls
Draw Marker
Draw Text
ตรวจ Vehicle
รอ Model โหลด
รอ Animation Dictionary
Periodic Cleanup
Update Cache
Background Logic
Polling ที่จำเป็น
Gameplay Loop
ตัวอย่างระบบ Shop
Thread
↓
ตรวจตำแหน่ง Player
↓
ใกล้ Shop หรือไม่
↓
แสดง Marker
↓
ตรวจปุ่ม E
↓
เปิด Shop
นี่เป็น Pattern FiveM ที่พบได้บ่อยมาก
③ CreateThread เป็น OS Thread จริงไหม
ไม่ควรมองว่าเป็น Operating System Thread แยกเต็มรูปแบบเหมือนการสร้าง Thread ระดับ OS
Wait() ของ CfxLua ทำงานโดยอาศัย Lua Coroutines
ดังนั้น Concept ที่เหมาะกับ Developer คือ
CfxLua Scheduler
↓
Coroutine / Thread
↓
Yield ด้วย Wait()
↓
กลับมาทำงานอีกครั้งภายหลัง
การเข้าใจเรื่อง Yield สำคัญกว่าคิดว่าเป็น CPU Thread แยกคนละ Core
④ CreateThread ต่างจาก Function ปกติอย่างไร
Function ปกติ เช่น
local function test()
print('Hello')
end
test()
จะถูกเรียกตาม Flow ปัจจุบัน
แต่
CreateThread(function()
-- asynchronous thread logic
end)
สร้าง Coroutine/Thread ใหม่ให้ Scheduler จัดการ
จึงเหมาะกับ Logic ที่ต้อง
รอ
วน
ทำงานเป็นระยะ
โดยไม่ต้อง Block Main Flow ของ Resource
⑤ ตัวอย่าง CreateThread แบบง่าย
CreateThread(function()
print('Hello from thread')
end)
เมื่อ Resource ทำงาน Thread จะถูกสร้างและ Handler จะถูก Execute
ถ้าต้องการ Delay
CreateThread(function()
Wait(5000)
print('5 seconds later')
end)
Code จะ Yield ก่อนประมาณ 5 วินาทีแล้วจึงทำงานต่อ
⑥ Citizen.CreateThread กับ CreateThread ต่างกันไหม
สำหรับ FiveM Lua คุณจะพบทั้ง
Citizen.CreateThread(function()
end)
และ
CreateThread(function()
end)
รูปแบบสั้นถูกใช้แพร่หลายใน Script ปัจจุบัน
เช่นเดียวกับ
Citizen.Wait(1000)
ที่สามารถเขียนเป็น
Wait(1000)
ได้
ดังนั้นถ้าเห็น Script เก่าและใหม่ใช้ Syntax ต่างกัน ไม่จำเป็นต้องคิดว่าเป็นระบบคนละชนิด
⑦ CreateThread ใช้กับ while true ได้อย่างไร
Pattern ที่พบมากที่สุดคือ
CreateThread(function()
while true do
Wait(1000)
print('Loop')
end
end)
Flow คือ
CreateThread
↓
while true
↓
ทำงาน
↓
Wait
↓
Yield
↓
Scheduler
↓
กลับมาทำงานต่อ
Loop สามารถทำงานต่อเนื่องได้โดยไม่พยายามยึด Execution ตลอดเวลา
⑧ ทำไม while true ต้องมี Wait
เพราะ Loop แบบนี้
CreateThread(function()
while true do
print('Loop')
end
end)
ไม่มีจุด Yield
Thread จะพยายามวนต่อเนื่องโดยไม่คืนการควบคุมให้ Scheduler
Cfx.re เตือนชัดเจนว่า while true do ที่ไม่มี Wait() สามารถทำให้ Client Game ค้างหรือ Crash ได้
ดังนั้นกฎพื้นฐานคือ
ถ้าเป็น Infinite Loop ใน CfxLua ต้องแน่ใจว่ามีเส้นทางที่เรียก
Wait()อย่างแน่นอน
⑨ ตัวอย่างที่อันตราย
CreateThread(function()
while true do
local ped = PlayerPedId()
end
end)
Code นี้ไม่มี
Wait()
จึงไม่ควรใช้
แก้ขั้นต่ำเป็น
CreateThread(function()
while true do
Wait(0)
local ped = PlayerPedId()
end
end)
แต่ยังต้องถามต่อว่า
จำเป็นต้องหา Player Ped ทุก Game Tick จริงหรือไม่?
Wait(0) ไม่ได้แปลว่า Optimize แล้ว
⑩ Wait คืออะไร
Wait() ใช้ Pause Execution ของ Coroutine ปัจจุบันตามจำนวน Milliseconds ที่กำหนด
ตัวอย่าง
Wait(1000)
หมายถึงให้ Thread Yield แล้วกลับมาทำงานต่อภายหลังประมาณ 1,000 ms
ตัวอย่าง
CreateThread(function()
print('Start')
Wait(2000)
print('2 seconds later')
end)
Wait() ทำงานภายใน Coroutine เช่น Thread ที่สร้างด้วย CreateThread()
⑪ Wait(0) คืออะไร
Cfx.re ระบุว่า
Wait(0)
จะ Pause Coroutine ปัจจุบันจนถึง Game Tick ถัดไป
ดังนั้น Pattern
CreateThread(function()
while true do
Wait(0)
-- code
end
end)
หมายถึง Logic ถูกประเมินประมาณทุก Game Tick
เหมาะกับงานที่จำเป็นต้องทำระดับ Frame/Tick จริง ๆ
⑫ Wait(1000) คืออะไร
Wait(1000)
หมายถึงพัก Thread ประมาณ 1 วินาที
ตัวอย่าง
CreateThread(function()
while true do
Wait(1000)
print('Every second')
end
end)
เหมาะกับ Logic ที่ไม่ต้อง Update ทุก Frame
เช่น
Refresh Cache
ตรวจ State บางชนิด
Cleanup
Periodic Check
⑬ Wait เป็นเวลาที่แม่น 100% ไหม
ไม่ควรมองว่า Wait(1000) เป็น Real-time Timer ที่มีความแม่นระดับ Hard Real-time
Cfx.re ใช้คำว่าพักประมาณตามจำนวน Milliseconds ที่กำหนด และ Scheduling ยังขึ้นกับ Runtime/Game Tick
ดังนั้นใช้ได้ดีสำหรับ Gameplay และ Scheduling ทั่วไป แต่ไม่ควรสร้างระบบที่ต้องการความแม่นระดับ Microsecond ด้วย Wait()
⑭ Game Tick คืออะไร
Game Tick คือรอบที่ระบบเกมและ Script Scheduler ทำงาน
สำหรับ Client เวลาแต่ละ Tickสัมพันธ์กับ Frame Rate ได้
Cfx.re ยกตัวอย่างว่า
60 FPS
≈ 16.6 ms ต่อ Tick
180 FPS
≈ 5.5 ms ต่อ Tick
ดังนั้นจำนวน Tick ต่อวินาทีไม่จำเป็นต้องเท่ากันทุกเครื่อง
นี่เป็นเหตุผลสำคัญว่าทำไม Wait(0) หมายถึง “Tick ถัดไป” ไม่ใช่จำนวน Milliseconds คงที่
⑮ ทำไม Wait(5) ไม่เท่ากับทุก Frame
ถ้าระบบต้องทำงานทุก Frame ควรใช้
Wait(0)
Cfx.re เตือนว่าการใช้
Wait(5)
Wait(10)
อาจพลาด Tick บนเครื่องที่ Frame Rate สูง
ดังนั้นถ้ากำลัง Draw บางสิ่งที่ต้อง Render ทุก Frame ควรเลือก Wait(0)
⑯ อะไรจำเป็นต้องใช้ Wait(0)
ตัวอย่างงานที่อาจต้องทำทุก Frame เช่น
DrawMarker
DrawText บางระบบ
ตรวจ Control ที่ต้องการ Response ต่อเนื่อง
DisableControlAction
Rendering บางประเภท
Scaleform Draw
Gameplay State บางชนิด
ตัวอย่าง
CreateThread(function()
while true do
Wait(0)
DrawMarker(
1,
100.0,
100.0,
30.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
1.0,
1.0,
1.0,
255,
255,
255,
150,
false,
false,
2,
false,
nil,
nil,
false
)
end
end)
Marker ที่ต้องแสดงต่อเนื่องมักต้อง Draw ซ้ำ
⑰ ทุก Thread ควร Wait(0) ไหม
ไม่
นี่เป็นข้อผิดพลาดที่พบบ่อย
สมมติแค่ต้อง Update Player Job Cache ทุก 5 วินาที
ไม่ควรทำ
CreateThread(function()
while true do
Wait(0)
-- update cache
end
end)
ควรเป็น
CreateThread(function()
while true do
Wait(5000)
-- update cache
end
end)
หรือดีกว่านั้น ใช้ Event Update เมื่อ Job เปลี่ยน หากระบบมี Event รองรับ
⑱ Event ดีกว่า Polling เมื่อไร
สมมติ Player Job เปลี่ยนเฉลี่ยไม่กี่ครั้งต่อ Session
ไม่จำเป็นต้องทำ
ทุก Tick
↓
เช็ก Job
ถ้า Framework มี Event
Job Changed
↓
Update Cache
Event-driven Approach จะลดงานที่ไม่จำเป็น
หลักคือ
ข้อมูลเปลี่ยนเฉพาะตอนมีเหตุการณ์
→ ใช้ Event
ข้อมูลต้องตรวจต่อเนื่องจริง
→ ใช้ Thread/Loop
⑲ Polling คืออะไร
Polling คือการตรวจ State ซ้ำเป็นช่วง ๆ
ตัวอย่าง
CreateThread(function()
while true do
Wait(1000)
local ped = PlayerPedId()
-- check something
end
end)
ทุกวินาที Script ถามว่า State เป็นอย่างไร
Polling ไม่ได้ผิด แต่ควรใช้ Interval ให้เหมาะสม
⑳ CreateThread กับ Player Coordinates
ตัวอย่างง่าย
CreateThread(function()
while true do
Wait(1000)
local ped =
PlayerPedId()
local coords =
GetEntityCoords(ped)
print(coords)
end
end)
Code นี้อ่านตำแหน่ง Player ทุก 1 วินาที
ถ้าเพียงต้อง Debug Coordinates อาจเพียงพอ
ไม่จำเป็นต้องใช้ Wait(0)
㉑ CreateThread กับ Marker
Marker เป็น Use Case ที่ดีสำหรับ Adaptive Thread
แทนที่จะ Draw/คำนวณหนักตลอดแม้ Player อยู่ไกล สามารถทำ
อยู่ไกล
→ Sleep นาน
เข้าใกล้
→ Sleep สั้น
ต้อง Draw
→ Wait(0)
ช่วยลด Client CPU
㉒ Adaptive Wait คืออะไร
ตัวอย่าง
local shopCoords =
vector3(
25.7,
-1345.2,
29.5
)
CreateThread(function()
while true do
local sleep = 1000
local ped =
PlayerPedId()
local coords =
GetEntityCoords(ped)
local distance =
#(coords - shopCoords)
if distance < 20.0 then
sleep = 0
DrawMarker(
1,
shopCoords.x,
shopCoords.y,
shopCoords.z - 1.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
1.0,
1.0,
1.0,
255,
255,
255,
150,
false,
false,
2,
false,
nil,
nil,
false
)
end
Wait(sleep)
end
end)
เมื่อ Player อยู่ไกล Thread Sleep 1 วินาที
เมื่ออยู่ใกล้ Marker จึงทำงานทุก Tick
㉓ Adaptive Wait ดีกว่า Wait(0) ตลอดอย่างไร
สมมติ Server มี Shop 100 จุด
ถ้า Script ทำ
ทุก Frame
↓
ตรวจ Shops ทั้งหมด
↓
คำนวณ Distance
↓
Native Calls
ตลอดเวลา Client อาจทำงานเกินจำเป็น
Adaptive Wait ช่วยลด Work เมื่อ Player ไม่ได้อยู่ใกล้ Feature นั้น
แต่ต้อง Optimize Algorithm ด้วย ไม่ใช่แค่เพิ่ม Sleep
㉔ ระวัง Bug ของ Adaptive Sleep
สมมติเขียน
local sleep = 0
CreateThread(function()
while true do
if condition then
sleep = 1000
end
Wait(sleep)
end
end)
แล้วไม่ได้ Reset sleep
Thread อาจคง Sleep 1000 ms แม้ Condition เปลี่ยนแล้ว
Pattern ที่ปลอดภัยกว่าคือกำหนดใหม่ทุก Loop
while true do
local sleep = 1000
-- logic
Wait(sleep)
end
จากนั้นลดค่าตามสถานการณ์
㉕ CreateThread กับ Controls
ตัวอย่างตรวจปุ่ม E
CreateThread(function()
while true do
Wait(0)
if IsControlJustReleased(
0,
38
) then
print('E released')
end
end
end)
เพราะ Input ที่ต้องการ Response ต่อ Frame มักใช้ Wait(0)
แต่ควรเปิด Logic นี้เฉพาะเมื่อจำเป็น
เช่นตรวจ E เฉพาะเมื่อ Player อยู่ใกล้ Interaction Point
㉖ ตัวอย่างตรวจปุ่มแบบ Optimize กว่า
local nearShop = false
CreateThread(function()
while true do
local sleep = 1000
if nearShop then
sleep = 0
if IsControlJustReleased(
0,
38
) then
TriggerEvent(
'shop:open'
)
end
end
Wait(sleep)
end
end)
ถ้า Player ไม่ได้อยู่ใกล้ Shop ก็ไม่ตรวจ Control ทุก Frame
㉗ CreateThread กับ Native
Native จำนวนมากถูกเรียกภายใน Thread
ตัวอย่าง
CreateThread(function()
while true do
Wait(500)
local ped =
PlayerPedId()
local inVehicle =
IsPedInAnyVehicle(
ped,
false
)
if inVehicle then
print('Player in vehicle')
end
end
end)
ต้องเลือก Interval ตามความไวที่ Feature ต้องการ
㉘ Native ทุกตัวควรเรียกทุก Tick ไหม
ไม่
Cfx.re แนะนำให้หลีกเลี่ยง Heavy Native Calls แบบ Per-tick หากไม่จำเป็น
บาง Native มี Cost สูงกว่า Native ง่าย ๆ
ดังนั้นควรถาม
Native นี้ต้องเรียกทุก Frameหรือไม่?
ถ้าไม่ ให้ลด Frequency
㉙ Cache Native Result ได้ไหม
ได้เมื่อเหมาะสม
ตัวอย่างแทนการเรียก PlayerPedId() ซ้ำหลายแห่งทุก Frame อาจมี Cache Thread
local playerPed = 0
CreateThread(function()
while true do
playerPed =
PlayerPedId()
Wait(1000)
end
end)
จากนั้น Code อื่นใช้
playerPed
แต่ต้องพิจารณาว่า Data ที่ Cache เปลี่ยนบ่อยแค่ไหน
Cfx.re เองยกการ Cache Native ที่เปลี่ยนไม่ถี่เป็นหนึ่งในแนวทางลด Native Calling Overhead
㉚ Cache PlayerPedId ตลอดไปดีไหม
ไม่ควรถือว่า Ped Handle ไม่เปลี่ยนตลอด Session
ระบบ Character/Ped Change อาจทำให้ Ped เปลี่ยนได้
จึงควร Refresh Cache เป็นระยะหรืออัปเดตตาม Event/Lifecycle ที่เหมาะสม
Cache มีประโยชน์ แต่ต้องไม่ทำให้ State เก่า
㉛ CreateThread ใช้รอ Model โหลดได้ไหม
ได้ และเป็น Pattern ที่พบมาก
local model =
joaat('sultan')
RequestModel(model)
while not HasModelLoaded(model) do
Wait(0)
end
Code นี้ต้องอยู่ใน Coroutine ที่สามารถ Wait() ได้ เช่น Thread หรือ Event Handler ที่รองรับ Yield ตาม Runtime
จากนั้นจึง Create Vehicle
㉜ ทำไมต้อง Wait ตอนโหลด Model
RequestModel() ไม่ได้หมายความว่า Model พร้อมใช้ทันที
จึงต้องตรวจ
HasModelLoaded(model)
ถ้ายังไม่พร้อม
Wait(...)
แล้วตรวจอีกครั้ง
Pattern คือ
Request
↓
ยังไม่โหลด
↓
Yield
↓
ตรวจใหม่
↓
โหลดแล้ว
↓
ทำงานต่อ
㉝ ควร Wait(0) หรือ Wait(500) ตอนโหลด Model
ขึ้นอยู่กับ Use Case
เอกสารตัวอย่าง Cfx.re มีการใช้ Wait() ภายใน Loop ระหว่างรอ Resource/Model
ถ้าต้องการ Response เร็วอาจใช้ Wait(0)
แต่ในบางกรณี Interval ที่มากขึ้นก็เพียงพอ
ต้องสมดุลระหว่าง Response Time และ Work ที่ทำซ้ำ
㉞ CreateThread ใช้บน Server ได้ไหม
ได้
Cfx.re ระบุ Citizen.CreateThread อยู่ทั้งใน Client-side และ Server-side Lua Functions
ตัวอย่าง Server
CreateThread(function()
while true do
Wait(60000)
print(
'Server periodic task'
)
end
end)
เหมาะกับ Background Task บางประเภท
㉟ Server Thread ใช้ทำอะไร
ตัวอย่าง
Periodic Cleanup
Refresh Cache
Scheduled Save
State Management
Server Maintenance Logic
Periodic Log
Checking Expired Sessions
แต่ไม่ควรใช้ Loop แทน Event หรือ Database Scheduler ทุกกรณี
㊱ Server Thread กิน CPU ได้ไหม
ได้
Server Loop ที่ทำงานหนักมีผลกับ Server มากกว่า Player คนเดียว
ตัวอย่างอันตราย
CreateThread(function()
while true do
Wait(0)
for _, playerId in ipairs(
GetPlayers()
) do
-- heavy work
end
end
end)
ถ้า Player จำนวนมากและ Logic หนัก Server CPU อาจเพิ่มสูงมาก
㊲ อย่าวน Player ทุก Tickถ้าไม่จำเป็น
สมมติมี Player 200 คน
Loop ทุก Tickหมายถึง Logic ถูกทำกับ Player จำนวนมากซ้ำอย่างรวดเร็ว
ถ้าระบบต้องตรวจเพียงทุก 5 วินาที ใช้
Wait(5000)
อาจเหมาะกว่า
หรือใช้ Event เมื่อ Player State เปลี่ยน
㊳ CreateThread กับ Database
ไม่ควรสร้าง Thread ที่ Query Database ถี่โดยไม่มีเหตุผล
ตัวอย่างที่ไม่ดี
CreateThread(function()
while true do
Wait(100)
-- SELECT database
end
end)
อาจสร้าง Database Load สูง
ถ้าข้อมูลสามารถ Cache ใน Memory ให้ Query เมื่อจำเป็นและ Update Cache ด้วย Event จะดีกว่าในหลายกรณี
㊴ Periodic Save ใช้ Thread ได้ไหม
ได้
ตัวอย่าง Concept
CreateThread(function()
while true do
Wait(300000)
-- save required data
end
end)
300,000 ms คือประมาณ 5 นาที
แต่ต้องออกแบบร่วมกับ
Player Disconnect Save
Resource Stop
Database Transactions
Error Handling
ไม่ควรพึ่ง Periodic Save เพียงอย่างเดียวสำหรับข้อมูลสำคัญ
㊵ CreateThread กับ SetTimeout ต่างกันอย่างไร
ถ้าต้องการทำงาน ครั้งเดียวในอนาคต อาจใช้ SetTimeout เหมาะกว่า Thread + Loop
Concept:
CreateThread + while
→ งานต่อเนื่อง
SetTimeout
→ งานครั้งเดียวหลัง Delay
ตัวอย่างที่ไม่จำเป็นต้องสร้าง Loop
SetTimeout(5000, function()
print('5 seconds later')
end)
เลือกเครื่องมือให้ตรงกับรูปแบบงาน
㊶ CreateThread กับ Event ต่างกันอย่างไร
Thread เหมาะกับ
งานต่อเนื่อง
งานเป็นช่วงเวลา
Polling
Rendering
Event เหมาะกับ
เกิดบางสิ่งขึ้น
↓
ทำงานทันที
ตัวอย่าง Job เปลี่ยน
ไม่จำเป็นต้อง Thread เช็ก Job ทุกวินาที หาก Framework มี Job Changed Event
ใช้ Event จะเหมาะกว่า
㊷ CreateThread กับ Callback ต่างกันอย่างไร
Thread เป็น Execution Flow
Callback เป็น Function ที่ถูกเรียกเมื่อ Operation หรือ Response พร้อม
ตัวอย่าง Database Async อาจทำ
Thread
↓
เรียก Async Operation
↓
Callback
↓
Result
ทั้งสองสามารถใช้ร่วมกัน แต่ไม่ใช่สิ่งเดียวกัน
㊸ CreateThread กับ Promise/Await
Thread ที่เป็น Coroutine สามารถใช้ Async Pattern ที่ Runtime รองรับ เช่น Citizen.Await
Concept
CreateThread
↓
Async Request
↓
Citizen.Await
↓
Yield
↓
Promise Resolve
↓
ทำงานต่อ
ช่วยเขียน Flow ที่ต้องรอ Result ได้อ่านง่ายขึ้น
แต่ต้องระวังไม่สร้าง Sequential Await จำนวนมากโดยไม่จำเป็น
㊹ CreateThread ซ้อน CreateThread ได้ไหม
ทำได้ทางเทคนิค
ตัวอย่าง
CreateThread(function()
CreateThread(function()
print('Child thread')
end)
end)
แต่ไม่ควรสร้าง Thread ซ้อนจำนวนมากโดยไม่มีเหตุผล
ถ้า Logic สามารถอยู่ Thread เดียวหรือใช้ Function/Event ได้ จะ Maintain ง่ายกว่า
㊺ สร้าง Thread ทุกครั้งที่ Player กดปุ่มดีไหม
ขึ้นอยู่กับว่า Thread จบหรือไม่
ตัวอย่างอันตราย:
ทุกครั้งที่กด E สร้าง
CreateThread(function()
while true do
Wait(0)
-- permanent loop
end
end)
กด 10 ครั้ง = อาจได้ 10 Infinite Threads
นี่เป็นสาเหตุที่ Resource บางตัว Resmon สูงขึ้นเรื่อย ๆ
㊻ Thread Leak คืออะไร
ในบริบท FiveM มักหมายถึงการสร้าง Thread ที่ไม่จบหรือไม่ถูกควบคุมจนจำนวน Thread สะสม
ตัวอย่าง
เปิด Menu
→ Thread 1
เปิด Menu อีกครั้ง
→ Thread 2
เปิดอีก
→ Thread 3
แต่ Thread เก่ายังทำงานอยู่
ผลคือ
CPU สูงขึ้น
Logic ซ้ำ
Event ซ้ำ
Native Calls ซ้ำ
ควรใช้ State ควบคุม Lifecycle
㊼ วิธีหยุด Loop ใน Thread
สามารถใช้ Condition
local running = true
CreateThread(function()
while running do
Wait(1000)
print('Running')
end
print('Thread finished')
end)
เมื่อต้องการหยุด
running = false
Loop จะจบเมื่อถึงรอบตรวจ Condition ถัดไป
㊽ ใช้ break ได้ไหม
ได้
ตัวอย่าง
CreateThread(function()
while true do
Wait(1000)
if shouldStop then
break
end
end
print('Stopped')
end)
เหมาะกับ Temporary Thread เช่น
Mission
Progress
Temporary UI
Temporary Interaction
ไม่จำเป็นต้องทำทุก Thread เป็น Infinite Loop
㊾ Temporary Thread ตัวอย่าง
CreateThread(function()
local endTime =
GetGameTimer() + 10000
while GetGameTimer() < endTime do
Wait(0)
-- temporary logic
end
print('Finished')
end)
Thread นี้ทำงานประมาณ 10 วินาทีแล้วจบ
เหมาะกว่าการสร้าง Permanent Thread หาก Feature มีอายุสั้น
㊿ CreateThread Error แล้ว Thread อื่นหยุดไหม
Error ใน Thread หนึ่งสามารถทำให้ Flow ของ Thread นั้นหยุดหรือเกิด Script Error แต่ไม่ได้หมายความว่า Runtime ทั้งหมดจะทำงานเหมือน Error ใน Function เดียวทุกกรณี
สิ่งสำคัญคือดู
Client
→ F8 Console
Server
→ Server Console
แล้วหาว่า Thread ไหนเกิด Error
51 วิธี Debug CreateThread
เพิ่ม Output จุดสำคัญ
CreateThread(function()
print('Thread started')
while true do
Wait(1000)
print('Thread tick')
end
end)
ถ้าเห็น
Thread started
แต่ไม่มี
Thread tick
ให้ตรวจ Error ก่อนหรือภายใน Loop
52 Thread ทำงานซ้ำผิดปกติตรวจอะไร
ตรวจว่า CreateThread() ถูกเรียกกี่ครั้ง
ตัวอย่าง
RegisterNetEvent(
'example:start',
function()
CreateThread(function()
while true do
Wait(1000)
print('Running')
end
end)
end
)
ถ้า Event example:start ถูกเรียก 20 ครั้ง จะสร้าง 20 Threads
ควรมี Guard เช่น
if running then
return
end
running = true
53 ตัวอย่าง Guard Thread
local running = false
local function startThread()
if running then
return
end
running = true
CreateThread(function()
while running do
Wait(1000)
print('Running')
end
end)
end
ช่วยป้องกัน Thread ถูกสร้างซ้ำโดยไม่ตั้งใจ
54 CreateThread กับ Resource Stop
เมื่อ Resource Stop Runtime ของ Resource จะถูกหยุดและ Thread ของ Resource ก็ไม่ควรคาดหวังว่าจะทำงานต่อใน Resource เดิม
แต่ถ้า Thread สร้าง Game Entity, Camera, NUI Focus หรือ State ภายนอกบางอย่าง ควรมี Cleanup ผ่าน Resource Lifecycle ตามความเหมาะสม
เช่น
ปิด NUI
Destroy Camera
ลบ Temporary Entity
Reset State
55 CreateThread กับ NUI
บาง Resource สร้าง Thread ตรวจว่า UI เปิดหรือไม่
แต่ไม่ควร Poll NUI Stateทุก Frameถ้า Event สามารถบอกได้
ตัวอย่าง
เปิด NUI
→ set uiOpen = true
ปิด NUI
→ set uiOpen = false
แล้ว Thread เฉพาะ Feature ที่ต้องทำ Per-frame ใช้ State นี้ตัดสิน
56 CreateThread กับ ESX
ESX Script ใช้ CreateThread() ได้ตาม CfxLua ปกติ
แต่ข้อมูล Framework ที่มี Event Update อยู่แล้ว เช่น Job/Player Loaded ไม่ควร Poll ทุก Tickโดยไม่มีเหตุผล
ตัวอย่าง
ESX Event
→ Update Cached Player Data
แล้ว Thread ใช้ Cache นั้น
จะดีกว่าเรียก Framework API หนัก ๆ ทุก Frameในหลายกรณี
57 CreateThread กับ QBCore
หลักเดียวกัน
QBCore มี Events สำหรับ Player Data หรือ Job State หลายส่วน
Resource ที่ดีควรผสม
Framework Events
+
Cached State
+
Thread เฉพาะที่จำเป็น
ไม่ใช่
ทุก Frame
→ ขอ QBCore Player Data ใหม่
ตลอดเวลา
58 CreateThread กับ Qbox
Qbox/ox ecosystem เน้น Event, State และ Library API หลายส่วน
การมี Tool ที่ทันสมัยไม่ได้หมายความว่า CreateThread() เลิกใช้
Thread ยังจำเป็นสำหรับ
Rendering
Controls
Nearby Interaction
Gameplay Loops
เพียงแต่ควรใช้เมื่อเป็นเครื่องมือที่เหมาะกับงาน
59 CreateThread Performance Checklist
ก่อนสร้าง Thread ใหม่ ให้ถาม
① ต้องใช้ Thread จริงหรือไม่?
② ใช้ Event แทนได้ไหม?
③ Loop ต้องทำทุก Frameหรือไม่?
④ ถ้าไม่ ต้อง Wait เท่าไร?
⑤ มี while true แต่ไม่มี Wait หรือไม่?
⑥ Native ที่เรียกหนักหรือไม่?
⑦ Cache ค่าได้หรือไม่?
⑧ Loop ผ่าน Entity/Player เยอะหรือไม่?
⑨ Thread ถูกสร้างซ้ำได้หรือไม่?
⑩ Thread ต้องมี Condition หยุดหรือไม่?
⑪ Query Database อยู่ใน Loop หรือไม่?
⑫ ส่ง Network Event จาก Loop ถี่เกินไปหรือไม่?
⑬ NUI Message ถูกส่งทุก Frameโดยไม่จำเป็นหรือไม่?
⑭ Profiler/Resmon แสดง Resource สูงหรือไม่?
Checklist นี้ช่วยลดปัญหา Script กิน CPU ได้มาก
⑥⓪ ตัวอย่าง CreateThread ที่เหมาะสำหรับมือใหม่
ลองสร้าง Command เปิด/ปิด Thread
local enabled = false
RegisterCommand(
'togglecoords',
function()
enabled =
not enabled
print(
'Coords thread:',
enabled
)
end,
false
)
CreateThread(function()
while true do
local sleep = 1000
if enabled then
sleep = 500
local ped =
PlayerPedId()
local coords =
GetEntityCoords(ped)
print(
('x: %.2f y: %.2f z: %.2f')
:format(
coords.x,
coords.y,
coords.z
)
)
end
Wait(sleep)
end
end)
เมื่อ
enabled = false
Thread Sleep 1 วินาที
เมื่อเปิด
enabled = true
จะอ่าน Coordinates ทุกครึ่งวินาที
นี่เป็นตัวอย่างที่ช่วยเข้าใจทั้ง
CreateThread
while
Wait
State
Native
Adaptive Sleep
ใน Script เดียว
คำถามที่พบบ่อยเกี่ยวกับ FiveM CreateThread
FiveM CreateThread คืออะไร
คือ Function ของ CfxLua สำหรับสร้าง Thread/Coroutine ใหม่ให้ Code ทำงานแบบ Asynchronous ภายใต้ Scheduler ของ FiveM
Citizen.CreateThread กับ CreateThread ต่างกันไหม
CreateThread เป็นรูปแบบ Alias/ชื่อสั้นที่ใช้แพร่หลาย สามารถพบทั้งสองรูปแบบใน FiveM Lua Script
CreateThread ใช้ Client ได้ไหม
ได้ และพบมากใน Gameplay, Marker, Controls และ Native Loops
CreateThread ใช้ Server ได้ไหม
ได้ Citizen.CreateThread อยู่ใน Server-side Lua Functions ของ Cfx.re เช่นกัน
while true ต้องมี Wait ไหม
ต้องมี Yield ที่แน่นอน Cfx.re เตือนว่า Infinite Loop โดยไม่มี Wait() สามารถทำให้ Client Game ค้างหรือ Crash ได้
Wait(0) คืออะไร
Yield Coroutine จนถึง Game Tick ถัดไป
Wait(1000) คืออะไร
พัก Thread ประมาณ 1 วินาทีแล้วกลับมาทำงานต่อ
ทุก Thread ควรใช้ Wait(0) ไหม
ไม่ ใช้ Wait(0) เฉพาะ Logic ที่จำเป็นต้องทำทุก Game Tick/Frame
Marker ต้องใช้ Wait(0) ไหม
การ Draw Marker ต่อเนื่องมักต้องทำทุก Frameในช่วงที่ต้องแสดง แต่สามารถใช้ Adaptive Sleep เมื่อ Player อยู่ไกลเพื่อประหยัด CPU
CreateThread ทำให้ FPS ตกไหม
ได้ถ้า Thread มี Heavy Loop, Native Calls จำนวนมาก หรือทำงานทุก Frameโดยไม่จำเป็น
สามารถสร้างหลาย Thread ได้ไหม
ได้ แต่ไม่ควรสร้าง Thread จำนวนมากหรือสร้าง Infinite Thread ซ้ำทุกครั้งที่ Event/Command ถูกเรียก
Thread หยุดอย่างไร
ใช้ Condition เช่น while running do แล้วเปลี่ยน running = false หรือใช้ break
Wait ใช้นอก CreateThread ได้ไหม
Wait() ต้องทำงานใน Coroutine Context ที่รองรับ Yield เช่น Thread; Cfx.re ระบุว่า Citizen.Wait ใช้ Lua Coroutines
CreateThread กับ Event ต่างกันอย่างไร
Thread เหมาะกับงานต่อเนื่อง/เป็นช่วง ส่วน Event เหมาะกับการทำงานเมื่อเหตุการณ์เกิดขึ้น
CreateThread กับ SetTimeout ต่างกันอย่างไร
Thread เหมาะกับงานต่อเนื่อง ส่วน SetTimeout เหมาะกับงานครั้งเดียวหลัง Delay
CreateThread Query Database ได้ไหม
ทำได้ใน Server Logic แต่ไม่ควร Query Database ถี่ใน Loop โดยไม่มีเหตุผล
Resmon สูงเกี่ยวกับ CreateThread ไหม
เกี่ยวได้ โดยเฉพาะ Resource ที่มี Loop Wait(0), Native จำนวนมาก หรือสร้าง Threads ซ้ำ อย่างไรก็ตามต้องใช้เครื่องมือวัดเพื่อหาต้นเหตุจริง
สรุป FiveM CreateThread คืออะไร
FiveM CreateThread() คือ Function สำหรับสร้าง Thread/Coroutine ใหม่ใน CfxLua เพื่อให้ Script สามารถทำ Logic แบบต่อเนื่อง เป็นช่วงเวลา หรือรอ Operation โดยไม่ Block Flow หลักของ Resource
รูปแบบพื้นฐานคือ
CreateThread(function()
while true do
Wait(1000)
-- logic
end
end)
หลักที่สำคัญที่สุดคือ
while true
+
ไม่มี Wait
=
อันตราย
และ
Wait(0)
=
Game Tick ถัดไป
ไม่ได้หมายความว่า Thread ทุกตัวควรใช้ Wait(0)
ถ้า Logic ไม่จำเป็นต้องทำทุก Frame ควรใช้ Interval ที่สูงขึ้น หรือเปลี่ยนเป็น Event-driven Logic
สำหรับ Performance ที่ดีควรคิดตามลำดับ
ต้องมี Thread ไหม?
↓
ต้อง Loop ไหม?
↓
ต้องทุก Frameไหม?
↓
ลด Native Calls ได้ไหม?
↓
Cache ได้ไหม?
↓
ใช้ Adaptive Wait ได้ไหม?
↓
ใช้ Event แทนได้ไหม?
สำหรับผู้ที่กำลังเรียน FiveM Developer กับ comsiam การเข้าใจ CreateThread() อย่างถูกต้องจะช่วยป้องกัน Script ที่ดูเหมือนทำงานปกติแต่กิน Client CPU อยู่ตลอดเวลา
โดยเฉพาะ Resource ที่มี Marker, Controls, Vehicle Checks และ Nearby Interaction ซึ่งมักใช้ Thread จำนวนมาก การเลือก Wait() และ Lifecycle ให้เหมาะสมมีผลต่อ FPS และ Resmon อย่างชัดเจน
และบทความถัดไปของ comsiam จะเข้าสู่หัวข้อที่เกี่ยวข้องโดยตรงคือ FiveM Wait คืออะไร ใช้อย่างไรไม่ให้ Script กิน CPU ซึ่งจะเจาะ Wait(0), Milliseconds, Game Tick, Coroutine และการเลือก Sleep Time ให้เหมาะกับแต่ละระบบ
Comments
Post a Comment