Up to date
This page is up to date for Godot 4.2
.
If you still find outdated information, please open an issue.
使用多執行緒¶
執行緒¶
執行緒允許同時執行程式碼。它允許從主執行緒解除安裝工作。
Godot 支援執行緒,並提供了許多方便使用的功能。
備註
如果使用其他語言(C#、C++),它們支援的執行緒類可能會更容易使用。
警告
在執行緒中使用內建類之前,請先閱讀 執行緒安全的 API,檢查在執行緒中使用是否安全。
建立內容¶
有下列事項需注意:
var thread: Thread
# The thread will start here.
func _ready():
thread = Thread.new()
# You can bind multiple arguments to a function Callable.
thread.start(_thread_function.bind("Wafflecopter"))
# Run here and exit.
# The argument is the bound data passed from start().
func _thread_function(userdata):
# Print the userdata ("Wafflecopter")
print("I'm a thread! Userdata is: ", userdata)
# Thread must be disposed (or "joined"), for portability.
func _exit_tree():
thread.wait_to_finish()
然後, 你的函式將在一個單獨的執行緒中運作, 直到它返回. 即使函式已經返回, 執行緒也必須收集它, 所以呼叫 Thread.wait_to_finish() , 它將等待中的執行緒完成(如果還沒有完成), 然後妥善處理它.
警告
在 Windows 上,在執行時間建立執行緒速度很慢,應該避免,以防止卡頓出現。應改用訊號量(本頁稍後將對此進行解釋)。
Mutex¶
並不總是支援從多個執行緒存取物件或資料(如果你這樣做, 會導致意外行為或當機). 請閱讀 執行緒安全的 API 文件, 瞭解哪些引擎API支援多執行緒存取.
在處理自己的資料或呼叫自己的函式時, 通常情況下, 儘量避免從不同的執行緒直接存取相同的資料. 你可能會遇到同步問題, 因為資料被修改後,CPU核之間並不總是更新. 當從不同執行緒存取一個資料時, 一定要使用 Mutex .
當呼叫 Mutex.lock() 時, 一個執行緒確保所有其他執行緒如果試圖 鎖 同一個mutex, 就會被阻塞(進入暫停狀態). 當通過呼叫 Mutex.unlock() 來解鎖該mutex時, 其他執行緒將被允許繼續鎖定(但每次只能鎖定一個).
下面是一個使用 Mutex 的例子:
var counter := 0
var mutex: Mutex
var thread: Thread
# The thread will start here.
func _ready():
mutex = Mutex.new()
thread = Thread.new()
thread.start(_thread_function)
# Increase value, protect it with Mutex.
mutex.lock()
counter += 1
mutex.unlock()
# Increment the value from the thread, too.
func _thread_function():
mutex.lock()
counter += 1
mutex.unlock()
# Thread must be disposed (or "joined"), for portability.
func _exit_tree():
thread.wait_to_finish()
print("Counter is: ", counter) # Should be 2.
Semaphore¶
有時你希望你的執行緒能“按需”工作。換句話說,告訴它什麼時候工作,讓它在不工作的時候暫停。為此,可以使用訊號量 Semaphore。執行緒中使用函式 Semaphore.wait() 來暫停它的工作,直到有資料到達。
而主執行緒則使用 Semaphore.post() 來表示資料已經準備好被處理:
var counter := 0
var mutex: Mutex
var semaphore: Semaphore
var thread: Thread
var exit_thread := false
# The thread will start here.
func _ready():
mutex = Mutex.new()
semaphore = Semaphore.new()
exit_thread = false
thread = Thread.new()
thread.start(_thread_function)
func _thread_function():
while true:
semaphore.wait() # Wait until posted.
mutex.lock()
var should_exit = exit_thread # Protect with Mutex.
mutex.unlock()
if should_exit:
break
mutex.lock()
counter += 1 # Increment counter, protect with Mutex.
mutex.unlock()
func increment_counter():
semaphore.post() # Make the thread process.
func get_counter():
mutex.lock()
# Copy counter, protect with Mutex.
var counter_value = counter
mutex.unlock()
return counter_value
# Thread must be disposed (or "joined"), for portability.
func _exit_tree():
# Set exit condition to true.
mutex.lock()
exit_thread = true # Protect with Mutex.
mutex.unlock()
# Unblock by posting.
semaphore.post()
# Wait until it exits.
thread.wait_to_finish()
# Print the counter.
print("Counter is: ", counter)