mirror of
https://github.com/godotengine/godot-docs.git
synced 2026-01-04 14:11:02 +03:00
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 <96648715+AThousandShips@users.noreply.github.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user