複数のスレッドの使用¶
スレッド¶
スレッドはコードの同時実行を可能にします。これにより、メインスレッドの負荷を軽減します。
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.
スレッドの作成¶
スレッドの作成は非常に簡単で、次のコードを使用するだけです:
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() を呼び出し、(まだ終了していない場合は)スレッドが終了するまで待機し、適切に破棄します。
ミューテックス¶
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
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)