瀏覽代碼

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():
     func _exit_tree():
         thread.wait_to_finish()
         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.
 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
 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
 :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()
         thread.wait_to_finish()
         print("Counter is: ", counter) # Should be 2.
         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
 Semaphores
 ----------
 ----------
 
 
@@ -188,3 +368,134 @@ ready to be processed:
 
 
         # Print the counter.
         # Print the counter.
         print("Counter is: ", 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;
+    }
+