Atomic Counters in GDScript

Our primary mechanism for managing state in GDScript is through signals and properties. However, there are other options for managing state. Here we’ll look at using a mutex for thread-safe counters accessed by multiple threads.

extends Node

var ops: int = 0
var mutex: Mutex
var threads: Array

func _ready():
    mutex = Mutex.new()
    
    # We'll start 50 threads that each increment the counter exactly 1000 times.
    for i in range(50):
        var thread = Thread.new()
        thread.start(self, "_increment_counter")
        threads.append(thread)
    
    # Wait until all the threads are done.
    for thread in threads:
        thread.wait_to_finish()
    
    # Here no threads are writing to 'ops', but we still use the mutex
    # to ensure we're reading the most up-to-date value.
    mutex.lock()
    print("ops:", ops)
    mutex.unlock()

func _increment_counter():
    for c in range(1000):
        # To safely increment the counter we use the mutex.
        mutex.lock()
        ops += 1
        mutex.unlock()

We expect to get exactly 50,000 operations. If we had used a non-thread-safe integer and incremented it without a mutex, we’d likely get a different number, changing between runs, because the threads would interfere with each other.

To run this script, attach it to a Node in your Godot scene and run the scene. You should see the following output in the console:

ops: 50000

Note that GDScript doesn’t have built-in atomic operations like Go does. Instead, we use a Mutex to ensure thread-safe access to our shared counter. This approach is more general and can be used for more complex operations, but it may have slightly more overhead than atomic operations for simple cases like this.

Also, keep in mind that Godot’s threading model is cooperative, not preemptive. This means that threads in GDScript won’t be interrupted mid-execution by the operating system. However, using proper synchronization is still important for correctness and to ensure that changes made in one thread are visible to other threads.

Next, we’ll look at other synchronization primitives available in GDScript for managing state in multithreaded scenarios.