Mutexes in GDScript

In the previous example, we saw how to manage simple counter state using atomic operations. For more complex state, we can use a mutex to safely access data across multiple threads.

extends Node

class_name Container

var mu = Mutex.new()
var counters = {"a": 0, "b": 0}

func inc(name: String) -> void:
    mu.lock()
    counters[name] += 1
    mu.unlock()

func _ready() -> void:
    var threads = []
    
    # This function increments a named counter in a loop
    var do_increment = func(name: String, n: int) -> void:
        for i in range(n):
            inc(name)
    
    # Run several threads concurrently; note that they all access
    # the same Container, and two of them access the same counter
    for i in range(3):
        var thread = Thread.new()
        if i < 2:
            thread.start(self, "do_increment", ["a", 10000])
        else:
            thread.start(self, "do_increment", ["b", 10000])
        threads.append(thread)
    
    # Wait for the threads to finish
    for thread in threads:
        thread.wait_to_finish()
    
    print(counters)

In this GDScript version:

  1. We define a Container class that holds a map of counters. We use a Mutex to synchronize access to this map.

  2. The inc method locks the mutex before accessing counters, and unlocks it afterwards. In GDScript, we don’t have a defer statement, so we explicitly unlock the mutex at the end of the function.

  3. In the _ready function (which is similar to main in GDScript), we create a Container instance and set up our counters.

  4. We define a do_increment function that increments a named counter in a loop.

  5. We create and start three threads, two incrementing counter “a” and one incrementing counter “b”.

  6. We wait for all threads to finish using wait_to_finish().

  7. Finally, we print the counters.

Running this program should show that the counters are updated as expected:

{a: 20000, b: 10000}

Note that in GDScript, we use threads instead of goroutines, and a Mutex instead of sync.Mutex. The concept is similar, but the implementation details differ slightly due to the differences between the languages.

Next, we’ll look at implementing this same state management task using only threads and signals (which are similar to channels in GDScript).