使用多线程
线程
线程允许同时执行代码。它允许从主线程卸载工作。
Godot 支持线程,并提供了许多方便使用的功能。
备注
如果使用其他语言(C#、C++),它们支持的线程类可能会更容易使用。
警告
在线程中使用内置类之前,请先阅读 线程安全的 API,检查在线程中使用是否安全。
创建线程
创建线程请使用如下代码:
GDScript
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 的例子:
GDScript
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() 来表示数据已经准备好被处理:
GDScript
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)