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++)を使用する場合、各言語がサポートしているスレッドクラスを使用する方が簡単な場合があります。

警告

Before using a built-in class in a thread, read Thread-safe APIs first to check whether it can be safely used in a thread.

スレッドの作成

To create a thread, use the following code:

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() を呼び出し、(まだ終了していない場合は)スレッドが終了するまで待機し、適切に破棄します。

警告

Creating threads at run-time is slow on Windows and should be avoided to prevent stuttering. Semaphores, explained later on this page, should be used instead.

ミューテックス

Accessing objects or data from multiple threads is not always supported (if you do it, it will cause unexpected behaviors or crashes). Read the Thread-safe APIs documentation to understand which engine APIs support multiple thread access.

独自のデータを処理するとき、または独自の関数を呼び出すときは、原則として、異なるスレッドから直接同じデータにアクセスしないようにしてください。データは変更時にCPUコア間で常に更新されないため、同期の問題が発生する可能性があります。異なるスレッドからデータにアクセスするときは、常にMutex を使用します。

Mutex.lock() を呼び出すと、スレッドは、同じミューテックスをロックしようとする、他の全てのスレッドをブロックされた(休止した)状態にします。Mutex.unlock() を呼び出してmutexのロックを解除すると、新たに他のスレッドがロックを続行できます(ただし、一度に1つのスレッドのみ)。

以下は、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.

セマフォ

スレッドを「オンデマンド」で動作させたい場合があります。言い換えれば、いつ作業するかを伝え、何もしていないときに休止させます。このために、Semaphores が使用されます。関数 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)