using_multiple_threads.rst 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. .. _doc_using_multiple_threads:
  2. Using Multiple Threads
  3. ======================
  4. Threads
  5. -------
  6. Threads allow simultaneous execution of code. It allows off-loading work.
  7. Godot supports threads and provides many handy functions for GDScript to use them.
  8. .. note:: If using other languages (C#, C++), it may be easier to use the threading classes they support.
  9. Creating a Thread
  10. ------------------
  11. Creating a thread is very simple, just use the following code:
  12. .. tabs::
  13. .. code-tab:: gdscript GDScript
  14. var thread = null
  15. # The thread will start here
  16. func _ready():
  17. thread = Thread.new()
  18. thread.start(self,"_thread_function","Wafflecopter")
  19. # Run here and exit
  20. func _thread_function(userdata):
  21. print("I'm a thread! Userdata is: ",userdata)
  22. # Thread must be disposed (or "Joined"), for portability
  23. func _exit_tree():
  24. thread.wait_to_finish()
  25. Your function will, then, run in a separate thread, then will exit.
  26. Even if the function exits, the thread must collect it, so call :ref:`Thread.wait_to_finish()<class_Thread_method_wait_to_finish>', which will wait until the thread is done (if not done yet), then collect it.
  27. Mutexes
  28. -------
  29. Accessing objects or data from multiple threads is not always supported (if you do it, it will cause unexpected behaviors or crashes). Read the :ref:`Thread Safe APIs<doc_thread_safe_apis>` to understand which engine APIs support multiple thread access.
  30. When processing your own data or calling your own functions, as a rule, try to avoid accessing the same data from different threads.
  31. Even if your data supports this, you may run into synchronization problems, as the data is not allways updated between CPU cores when modified. Always use a :ref`Mutex<class_Mutex>` when accessing a piece of data from different threads.
  32. Here is an example of using a mutex to access data:
  33. .. tabs::
  34. .. code-tab:: gdscript GDScript
  35. var counter = 0
  36. var mutex = null
  37. var thread = null
  38. # The thread will start here
  39. func _ready():
  40. mutex = Mutex.new()
  41. thread = Thread.new()
  42. thread.start(self,"_thread_function","Wafflecopter")
  43. #increase value, protect it with mutex
  44. mutex.lock()
  45. counter+=1
  46. mutex.unlock()
  47. # Increment the value from the thread, too
  48. func _thread_function(userdata):
  49. mutex.lock()
  50. counter+=1
  51. mutex.unlock()
  52. # Thread must be disposed (or "Joined"), for portability
  53. func _exit_tree():
  54. thread.wait_to_finish()
  55. print("Counter is: ",counter) # Should be 2
  56. Semaphores
  57. -----------
  58. Sometimes you want your thread to work *"On Demand"*. In other words, tell it when to work and let it suspend when it doesn't.
  59. For this *:ref`Semaphores<class_Semaphore>`* are used. The function *wait()* is used in the thread to suspend it until some data arrives.
  60. The main thread, instead, uses *post()* to signal that data is ready:
  61. .. tabs::
  62. .. code-tab:: gdscript GDScript
  63. var counter = 0
  64. var mutex = null
  65. var semaphore = null
  66. var thread = null
  67. var exit_thread = false
  68. # The thread will start here
  69. func _ready():
  70. mutex = Mutex.new()
  71. semaphore = Semaphore.new()
  72. exit_thread=false
  73. thread = Thread.new()
  74. thread.start(self,"_thread_function","Wafflecopter")
  75. func _thread_function(userdata):
  76. while(true):
  77. semaphore.wait() # wait until posted
  78. mutex.lock()
  79. var should_exit = exit_thread # protect with mutex
  80. mutex.unlock()
  81. if (should_exit):
  82. break
  83. mutex.lock()
  84. counter+=1 # increment counter, protect with mutex
  85. mutex.unlock()
  86. func increment_counter():
  87. semaphore.post() # Make the thread process
  88. func get_counter():
  89. mutex.lock()
  90. # copy counter, protect with mutex
  91. var counter_value = counter
  92. mutex.unlock()
  93. return counter_value
  94. # Thread must be disposed (or "Joined"), for portability
  95. func _exit_tree():
  96. # Set exit condition to true
  97. mutex.lock()
  98. exit_thread = true # protect with mutex
  99. mutex.unlock()
  100. # unblock by posting
  101. semaphore.post()
  102. # wait until it exits
  103. thread.wait_to_finish()
  104. # Print the counter
  105. print("Counter is: ",counter)