瀏覽代碼

Update using_multiple_threads.rst (#8752)

* Update using_multiple_threads.rst

Update using_multiple_threads.rst

Adding C++ demos. It doesn't mirror the GDScript exactly, as I found this to be a clearer example of what the thread was doing when testing with multiple threads. All credit to "coder" from this Godot Forum thread: https://forum.godotengine.org/t/gdextension-c-async/36756/7?u=i-snyder

With applied suggestions from AThousandShips code review (many thanks!!)

---------

Co-authored-by: A Thousand Ships <[email protected]>
Ian Snyder 8 月之前
父節點
當前提交
7855e17267
共有 1 個文件被更改,包括 311 次插入0 次删除
  1. 311 0
      tutorials/performance/using_multiple_threads.rst

+ 311 - 0
tutorials/performance/using_multiple_threads.rst

@@ -47,6 +47,93 @@ To create a thread, use the following code:
     func _exit_tree():
         thread.wait_to_finish()
 
+ .. code-tab:: cpp C++ .H File
+
+    #ifndef MULTITHREADING_DEMO_H
+    #define MULTITHREADING_DEMO_H
+
+    #include <godot_cpp/classes/node.hpp>
+    #include <godot_cpp/classes/thread.hpp>
+
+    namespace godot {
+        class MultithreadingDemo : public Node {
+            GDCLASS(MultithreadingDemo, Node);
+
+        private:
+            Ref<Thread> worker;
+
+        protected:
+            static void _bind_methods();
+            void _notification(int p_what);
+
+        public:
+            MultithreadingDemo();
+            ~MultithreadingDemo();
+
+            void demo_threaded_function();
+        };
+    } // namespace godot
+
+    #endif // MULTITHREADING_DEMO_H
+
+ .. code-tab:: cpp C++ .CPP File
+
+    #include "multithreading_demo.h"
+
+    #include <godot_cpp/classes/engine.hpp>
+    #include <godot_cpp/classes/os.hpp>
+    #include <godot_cpp/classes/time.hpp>
+    #include <godot_cpp/core/class_db.hpp>
+    #include <godot_cpp/variant/utility_functions.hpp>
+
+    using namespace godot;
+
+    void MultithreadingDemo::_bind_methods() {
+        ClassDB::bind_method(D_METHOD("threaded_function"), &MultithreadingDemo::demo_threaded_function);
+    }
+
+    void MultithreadingDemo::_notification(int p_what) {
+        // Prevents this from running in the editor, only during game mode. In Godot 4.3+ use Runtime classes.
+        if (Engine::get_singleton()->is_editor_hint()) {
+            return;
+        }
+
+        switch (p_what) {
+            case NOTIFICATION_READY: {
+                worker.instantiate();
+                worker->start(callable_mp(this, &MultithreadingDemo::demo_threaded_function), Thread::PRIORITY_NORMAL);
+            } break;
+            case NOTIFICATION_EXIT_TREE: { // Thread must be disposed (or "joined"), for portability.
+                // Wait until it exits.
+                if (worker.is_valid()) {
+                    worker->wait_to_finish();
+                }
+
+                worker.unref();
+            } break;
+        }
+    }
+
+    MultithreadingDemo::MultithreadingDemo() {
+        // Initialize any variables here.
+    }
+
+    MultithreadingDemo::~MultithreadingDemo() {
+        // Add your cleanup here.
+    }
+
+    void MultithreadingDemo::demo_threaded_function() {
+        UtilityFunctions::print("demo_threaded_function started!");
+        int i = 0;
+        uint64_t start = Time::get_singleton()->get_ticks_msec();
+        while (Time::get_singleton()->get_ticks_msec() - start < 5000) {
+            OS::get_singleton()->delay_msec(10);
+            i++;
+        }
+
+        UtilityFunctions::print("demo_threaded_function counted to: ", i, ".");
+    }
+
 Your function will, then, run in a separate thread until it returns.
 Even if the function has returned already, the thread must collect it, so call
 :ref:`Thread.wait_to_finish()<class_Thread_method_wait_to_finish>`, which will
@@ -112,6 +199,99 @@ Here is an example of using a Mutex:
         thread.wait_to_finish()
         print("Counter is: ", counter) # Should be 2.
 
+ .. code-tab:: cpp C++ .H File
+
+    #ifndef MUTEX_DEMO_H
+    #define MUTEX_DEMO_H
+
+    #include <godot_cpp/classes/mutex.hpp>
+    #include <godot_cpp/classes/node.hpp>
+    #include <godot_cpp/classes/thread.hpp>
+
+    namespace godot {
+        class MutexDemo : public Node {
+            GDCLASS(MutexDemo, Node);
+
+        private:
+            int counter = 0;
+            Ref<Mutex> mutex;
+            Ref<Thread> thread;
+
+        protected:
+            static void _bind_methods();
+            void _notification(int p_what);
+
+        public:
+            MutexDemo();
+            ~MutexDemo();
+
+            void thread_function();
+        };
+    } // namespace godot
+
+    #endif // MUTEX_DEMO_H
+
+ .. code-tab:: cpp C++ .CPP File
+
+    #include "mutex_demo.h"
+
+    #include <godot_cpp/classes/engine.hpp>
+    #include <godot_cpp/classes/time.hpp>
+    #include <godot_cpp/core/class_db.hpp>
+    #include <godot_cpp/variant/utility_functions.hpp>
+
+    using namespace godot;
+
+    void MutexDemo::_bind_methods() {
+        ClassDB::bind_method(D_METHOD("thread_function"), &MutexDemo::thread_function);
+    }
+
+    void MutexDemo::_notification(int p_what) {
+        // Prevents this from running in the editor, only during game mode.
+        if (Engine::get_singleton()->is_editor_hint()) {
+            return;
+        }
+
+        switch (p_what) {
+            case NOTIFICATION_READY: {
+                UtilityFunctions::print("Mutex Demo Counter is starting at: ", counter);
+                mutex.instantiate();
+                thread.instantiate();
+                thread->start(callable_mp(this, &MutexDemo::thread_function), Thread::PRIORITY_NORMAL);
+
+                // Increase value, protect it with Mutex.
+                mutex->lock();
+                counter += 1;
+                UtilityFunctions::print("Mutex Demo Counter is ", counter, " after adding with Mutex protection.");
+                mutex->unlock();
+            } break;
+            case NOTIFICATION_EXIT_TREE: { // Thread must be disposed (or "joined"), for portability.
+                // Wait until it exits.
+                if (thread.is_valid()) {
+                    thread->wait_to_finish();
+                }
+                thread.unref();
+
+                UtilityFunctions::print("Mutex Demo Counter is ", counter, " at EXIT_TREE."); // Should be 2.
+            } break;
+        }
+    }
+
+    MutexDemo::MutexDemo() {
+        // Initialize any variables here.
+    }
+
+    MutexDemo::~MutexDemo() {
+        // Add your cleanup here.
+    }
+
+    // Increment the value from the thread, too.
+    void MutexDemo::thread_function() {
+        mutex->lock();
+        counter += 1;
+        mutex->unlock();
+    }
+
 Semaphores
 ----------
 
@@ -188,3 +368,134 @@ ready to be processed:
 
         # Print the counter.
         print("Counter is: ", counter)
+
+ .. code-tab:: cpp C++ .H File
+
+    #ifndef SEMAPHORE_DEMO_H
+    #define SEMAPHORE_DEMO_H
+
+    #include <godot_cpp/classes/mutex.hpp>
+    #include <godot_cpp/classes/node.hpp>
+    #include <godot_cpp/classes/semaphore.hpp>
+    #include <godot_cpp/classes/thread.hpp>
+
+    namespace godot {
+        class SemaphoreDemo : public Node {
+            GDCLASS(SemaphoreDemo, Node);
+
+        private:
+            int counter = 0;
+            Ref<Mutex> mutex;
+            Ref<Semaphore> semaphore;
+            Ref<Thread> thread;
+            bool exit_thread = false;
+
+        protected:
+            static void _bind_methods();
+            void _notification(int p_what);
+
+        public:
+            SemaphoreDemo();
+            ~SemaphoreDemo();
+
+            void thread_function();
+            void increment_counter();
+            int get_counter();
+        };
+    } // namespace godot
+
+    #endif // SEMAPHORE_DEMO_H
+
+ .. code-tab:: cpp C++ .CPP File
+
+    #include "semaphore_demo.h"
+
+    #include <godot_cpp/classes/engine.hpp>
+    #include <godot_cpp/classes/time.hpp>
+    #include <godot_cpp/core/class_db.hpp>
+    #include <godot_cpp/variant/utility_functions.hpp>
+
+    using namespace godot;
+
+    void SemaphoreDemo::_bind_methods() {
+        ClassDB::bind_method(D_METHOD("thread_function"), &SemaphoreDemo::thread_function);
+    }
+
+    void SemaphoreDemo::_notification(int p_what) {
+        // Prevents this from running in the editor, only during game mode.
+        if (Engine::get_singleton()->is_editor_hint()) {
+            return;
+        }
+
+        switch (p_what) {
+            case NOTIFICATION_READY: {
+                UtilityFunctions::print("Semaphore Demo Counter is starting at: ", counter);
+                mutex.instantiate();
+                semaphore.instantiate();
+                exit_thread = false;
+
+                thread.instantiate();
+                thread->start(callable_mp(this, &SemaphoreDemo::thread_function), Thread::PRIORITY_NORMAL);
+
+                increment_counter(); // Call increment counter to test.
+            } break;
+            case NOTIFICATION_EXIT_TREE: { // Thread must be disposed (or "joined"), for portability.
+                // Set exit condition to true.
+                mutex->lock();
+                exit_thread = true; // Protect with Mutex.
+                mutex->unlock();
+
+                // Unblock by posting.
+                semaphore->post();
+
+                // Wait until it exits.
+                if (thread.is_valid()) {
+                    thread->wait_to_finish();
+                }
+                thread.unref();
+
+                // Print the counter.
+                UtilityFunctions::print("Semaphore Demo Counter is ", get_counter(),  " at EXIT_TREE.");
+            } break;
+        }
+    }
+
+    SemaphoreDemo::SemaphoreDemo() {
+        // Initialize any variables here.
+    }
+
+    SemaphoreDemo::~SemaphoreDemo() {
+        // Add your cleanup here.
+    }
+
+    // Increment the value from the thread, too.
+    void SemaphoreDemo::thread_function() {
+        while (true) {
+            semaphore->wait(); // Wait until posted.
+
+            mutex->lock();
+            bool should_exit = exit_thread; // Protect with Mutex.
+            mutex->unlock();
+
+            if (should_exit) {
+                break;
+            }
+
+            mutex->lock();
+            counter += 1; // Increment counter, protect with Mutex.
+            mutex->unlock();
+        }
+    }
+
+    void SemaphoreDemo::increment_counter() {
+        semaphore->post(); // Make the thread process.
+    }
+
+    int SemaphoreDemo::get_counter() {
+        mutex->lock();
+        // Copy counter, protect with Mutex.
+        int counter_value = counter;
+        mutex->unlock();
+        return counter_value;
+    }
+