The timing of this video couldn't have been better. I recently put multithreading into my latest game and although I had a rough idea of semaphore and mutexes, and especially with semaphores, this video made it crystal clear. Originally I had thought the threads are used in a different way, where one creates them only when necessary and then destroys them, although the uses I'm seeing are creating more threads from the get go, and keeping them alive/waiting/working throughout the entirety of your game loop.
@@bitbirdy I'm building a roguelite, and atm it's being used for the proc. generation of rooms before the player gets to them. Now that I know a bit more about their use, I'll keep them handy and see if there's anything else. The fact that no two threads can be touching the same thing gets me to cry sometimes though
Wow! Amazing explanation! Two questions... Why do the consumers and producers wait so long to start consuming/producing (shenanigans) when not using the semaphores? Does it matter if you put the xxx_post() commands inside the mutex.lock/unlock vs. outside (like you have it)?
Hey thanks! Here are some hopefully adequate answers: Question 1 - the producers and consumers actually start producing/consuming right away when you run it the program. You'll see at 5:55 that the producers produce enough times to fill up the buffer. If you're asking why it takes so long for the consumers to get ahold of the mutex after that, sadly I'm not sure why 🤷 Question 2 - If you put the xxx_post() commands inside the mutex the behavior seems to be exactly the same. This makes sense because even if a semaphore has let a thread through, the mutex will block it from continuing, so that thread has only progressed past the "if should_exit: break" line. I think we should only put code in a mutex if it needs to be inside.
hi btw just for looks you can change these lines produce function: print("producer (",OS.get_thread_caller_id(), ") produced: (", x, ") in buffer at: (",buffer.size(), ")") consume function: var x = buffer.front() buffer.pop_front() print("consumer (",OS.get_thread_caller_id(), ") consumed: (", x, ") in buffer")
Maybe you should also make a video about a ringbuffer, which I believe is a better way of solving the Producer/Consumer problem and doesn't require constant locking.
You just explained the producer consumer problem 10x better than the lecturer did in my Systems Programming class. Keep up the awesome content!!
Come back, you are gem on youtube ❤
The timing of this video couldn't have been better. I recently put multithreading into my latest game and although I had a rough idea of semaphore and mutexes, and especially with semaphores, this video made it crystal clear.
Originally I had thought the threads are used in a different way, where one creates them only when necessary and then destroys them, although the uses I'm seeing are creating more threads from the get go, and keeping them alive/waiting/working throughout the entirety of your game loop.
Im glad it was helpful! Just curious, what are you using multithreading for in your game?
@@bitbirdy I'm building a roguelite, and atm it's being used for the proc. generation of rooms before the player gets to them. Now that I know a bit more about their use, I'll keep them handy and see if there's anything else. The fact that no two threads can be touching the same thing gets me to cry sometimes though
@@KrystofKlestil1337 the same thing makes me cry also 😂
@@KrystofKlestil1337 That's pretty cool, I hope getting the multithreading to work wasn't tooooo agonizing lol. Good luck with your game!
Thank you so much for this.
Thanks for this.
this is gold, thanks for video.
Excellent video! Keep them coming :)
Thank you
This could be used to put together a proposal to enhance the existing docs.
GODOOOWD! Nice tutorial though. godough.
Awesome tutorial.. is these features (threads, semaphore) in Godot work on all platforms (mainly android )?
I believe so, but there might be a limit in how many threads you can have running at the same time.
@@bitbirdy thanks for reply
Wow! Amazing explanation! Two questions... Why do the consumers and producers wait so long to start consuming/producing (shenanigans) when not using the semaphores? Does it matter if you put the xxx_post() commands inside the mutex.lock/unlock vs. outside (like you have it)?
Hey thanks! Here are some hopefully adequate answers:
Question 1 - the producers and consumers actually start producing/consuming right away when you run it the program. You'll see at 5:55 that the producers produce enough times to fill up the buffer. If you're asking why it takes so long for the consumers to get ahold of the mutex after that, sadly I'm not sure why 🤷
Question 2 - If you put the xxx_post() commands inside the mutex the behavior seems to be exactly the same. This makes sense because even if a semaphore has let a thread through, the mutex will block it from continuing, so that thread has only progressed past the "if should_exit: break" line. I think we should only put code in a mutex if it needs to be inside.
hi
btw just for looks you can change these lines
produce function:
print("producer (",OS.get_thread_caller_id(), ") produced: (", x, ") in buffer at: (",buffer.size(), ")")
consume function:
var x = buffer.front()
buffer.pop_front()
print("consumer (",OS.get_thread_caller_id(), ") consumed: (", x, ") in buffer")
Are you alive?)
Maybe you should also make a video about a ringbuffer, which I believe is a better way of solving the Producer/Consumer problem and doesn't require constant locking.
talking too fast- doesn't give us time to understand what you're saying.
Thank you