使用多线程

螺纹

线程允许同时执行代码。它允许从主线程卸载工作。

Godot支持线程,并提供许多方便的函数来使用它们。

注解

如果使用其他语言(C#,C++),使用他们支持的线程类可能更容易。

创建线程

创建线程非常简单,只需使用以下代码:

var thread

# The thread will start here.
func _ready():
    thread = Thread.new()
    # Third argument is optional userdata, it can be any variable.
    thread.start(self, "_thread_function", "Wafflecopter")

# Run here and exit.
# The argument is the userdata passed from start().
# If no argument was passed, this one still needs to
# be here and it will be null.
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() ,它将等待线程完成(如果尚未完成),然后正确地处理它。

互斥量

不总是支持从多个线程访问对象或数据(如果这样做,将导致意外的行为或崩溃)。阅读 Thread safe APIs 了解哪些引擎API支持多线程访问。

通常,在处理自己的数据或调用自己的函数时,尽量避免直接从不同的线程访问相同的数据。您可能会遇到同步问题,因为修改时CPU核心之间的数据并不总是更新的。始终使用 Mutex 当从不同的线程访问一段数据时。

打电话时 Mutex.lock() ,线程确保所有其他线程在尝试时都将被阻止(置于挂起状态)。 lock 相同的互斥。当通过呼叫解锁互斥锁时 Mutex.unlock() ,其他线程将允许继续使用锁(但一次只能进行一个)。

下面是使用互斥体的示例:

var counter = 0
var mutex
var thread

# The thread will start here.
func _ready():
    mutex = Mutex.new()
    thread = Thread.new()
    thread.start(self, "_thread_function")

    # Increase value, protect it with Mutex.
    mutex.lock()
    counter += 1
    mutex.unlock()

# Increment the value from the thread, too.
func _thread_function(userdata):
    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.

信号量

有时候你想让你的线工作 “按需” . 换句话说,告诉它什么时候工作,什么都不做就让它暂停。为此, Semaphores 使用。功能 Semaphore.wait() 在线程中用于挂起它,直到某些数据到达。

相反,主线程使用 Semaphore.post() 要表示数据已准备好处理,请执行以下操作:

var counter = 0
var mutex
var semaphore
var 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(self, "_thread_function")

func _thread_function(userdata):
    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)