C++ Multi Threading Part 2: Mutex And Conditional Variables

Поділитися
Вставка
  • Опубліковано 25 січ 2025

КОМЕНТАРІ • 83

  • @VincentBonaventura-z8u
    @VincentBonaventura-z8u Рік тому +4

    I've been reading a TON of substack posts on these subjects and I found myself simply getting MORE confused... you really do a great job getting to the point in a clear and illustrative way!

  • @vijayap1929
    @vijayap1929 19 днів тому

    amazing video! very clear explanations of stl multithreading capabilities!

  • @gregwoolley
    @gregwoolley Рік тому

    I agree with the other commenters. You explain this really well. Your explanation is by far the most natural, simplest yet most comprehensive and detailed explanation I've come across. Big thank you. Please keep up your good work.

  • @deepikakumar8369
    @deepikakumar8369 2 місяці тому

    Excellent video ! hats off for your explanation and your knowledge... Great Work!

  • @moinmemon5764
    @moinmemon5764 3 роки тому +6

    Best video to understand memory model, thread synchronisation. So impressed.
    Everything is crystal clear n organised. Keep up the good work man ✌🏻

  • @Kazner0h
    @Kazner0h Рік тому

    I was having such a hard time understanding this, but your effective and comprehensive breakdown got through to me.

  • @teetanrobotics5363
    @teetanrobotics5363 4 роки тому +1

    Hands Down. One of the best professors on the planet.

  • @TheJank7
    @TheJank7 3 роки тому

    Such a good video, the least I can do is to leave a comment and give a thumbs up. How did some many people not leave a like?

  • @muhammadalialam2846
    @muhammadalialam2846 Рік тому

    Amazing video, very informative, explained everything in clearly. Thank you for this video :)

  • @kristijanceple6026
    @kristijanceple6026 4 роки тому +4

    This is truly genius and so good! I'm really glad for discovering this channel

  • @kevdaag2523
    @kevdaag2523 4 роки тому +1

    At 31:25, in your example the notify_one() from the consumer to the producer is not necessary. The producer thread will reach the top of its loop and try to aqcuire the lock again, where it will be block until the consumer unlocks.

    • @MaxRob27
      @MaxRob27 Рік тому

      notify_one() from the consumer (and so wait() in producer) is useful because of the possibility of the producer to produce 2 data while the consumer is consuming the first. If there weren't these two because consumer can consume data without possesing the lock (so it can consume while the producer produce another data) the producer could start another cycle of the while loop thus producing another data and overwrite the previous one that the consumer could never touch. It's been 3 years but i answered anyway.

    • @MaxRob27
      @MaxRob27 Рік тому

      For the purpose i wrote a code that basically implement the consumer-producer logic shown in the video and this is an output with your changes:
      Main thread started
      Producer thread started
      Data produced: 290
      Data produced: 575
      Data produced: 957
      Data produced: 361
      Data produced: 924
      Data produced: 393
      Data produced: 648
      Data produced: 169
      Data produced: 347
      Data produced: 716
      Data produced: 8
      Data produced: 540
      Data produced: 839
      Data produced: 109
      Data produced: 3
      Data produced: 701
      Data produced: 643
      Data produced: 176
      Data produced: 431
      Data produced: 864
      Data produced: 640
      Data produced: 780
      Data produced: 733
      Data produced: 170
      Data produced: 193
      Data produced: 102
      Data produced: 856
      Data produced: 242
      Data produced: 129
      Data produced: 868
      Data produced: 519
      Data produced: 388
      Data produced: 361
      Data produced: 204
      Data produced: 944
      Data produced: 692
      Data produced: 747
      Data produced: 915
      Data produced: 133
      Data produced: 7
      Data produced: 572
      Data produced: 986
      Data produced: 695
      Data produced: 347
      Data produced: 220
      Consumer thread started
      Data consumed: 220
      Data produced: 127
      Data produced: 829
      Data produced: 500
      Data produced: 990
      Data produced: 749
      Data produced: 694
      Data produced: 259
      Data produced: 598
      Data produced: 100
      Data produced: 659
      Data produced: 399
      Data produced: 483
      Data produced: 462
      Data produced: 353
      Data produced: 910
      Data produced: 498
      Data produced: 860
      Data produced: 759
      Data produced: 988
      Data produced: 626
      Data produced: 930
      Data produced: 406
      Data produced: 589
      Data produced: 946
      Data produced: 442
      Data produced: 879
      Data produced: 710
      Data produced: 646
      Data produced: 451
      Data produced: 41
      Data produced: 454
      Data produced: 693
      Data produced: 259
      Data produced: 29
      Data produced: 990
      Data produced: 712
      Data produced: 666
      Data produced: 145
      Data produced: 319
      Data produced: 796
      Data produced: 162
      Data produced: 381
      Data produced: 19
      Data produced: 420
      Data produced: 405
      Data produced: 393
      Data produced: 335
      Data produced: 855
      Data produced: 62
      Data produced: 785
      Data produced: 169
      Data produced: 344
      Data produced: 9
      Data produced: 370
      Data produced: 840
      Data produced: 900
      Data produced: 790
      Data produced: 766
      Data produced: 164
      Data produced: 645
      Producer thread terminated
      Data consumed: 645
      Consumer thread terminated
      Main thread terminated
      While this is an output with the producer waiting for the consumer notification:
      Main thread started
      Consumer thread started
      Producer thread started
      Data produced: 964
      Data consumed: 964
      Data produced: 597
      Data consumed: 597
      Data produced: 186
      Data consumed: 186
      Data produced: 296
      Data consumed: 296
      Data produced: 620
      Data consumed: 620
      Data produced: 209
      Data consumed: 209
      Data produced: 93
      Data consumed: 93
      Data produced: 228
      Data consumed: 228
      Data produced: 510
      Data consumed: 510
      Data produced: 2
      Data consumed: 2
      Data produced: 709
      Data consumed: 709
      Data produced: 185
      Data consumed: 185
      Data produced: 459
      Data consumed: 459
      Data produced: 452
      Data consumed: 452
      Data produced: 331
      Data consumed: 331
      Data produced: 606
      Data consumed: 606
      Data produced: 680
      Data consumed: 680
      Data produced: 600
      Data consumed: 600
      Consumer thread terminated
      Producer thread terminated
      Main thread terminated
      Pretty clear why it's useful now. As you can see way less data are produced in second case (the original) but every data is consumed at least (In each case the program is run for 1 ms).

  • @wolverine9632
    @wolverine9632 3 роки тому +1

    As soon as I saw the intro, I knew this was going to be some high quality content. AND YOU DIDN'T DISAPPOINT!
    Thank you very much, sir, from a new subscriber!

  • @alexstephens9475
    @alexstephens9475 2 роки тому +2

    Top tier explanation. I love that you explained many relevant things along with the concepts mentioned in the title, making a comprehensive learning experience instead of a fragmented one. Subscribed! :)

  • @rbaccount4878
    @rbaccount4878 4 роки тому +4

    Fantastic stuff! I really appreciate your efforts and time to educate others in a very clear way with the visuals.
    Hats off :)
    I have a question. At 31:46, in consumer(), why "ul.lock()" is needed after ConsumerData(data) ? Since its end of the while loop iteration, ul will be out of scope anyways and new ul.lock will happen at the start of the while loop.

    • @sangamchoudhary6977
      @sangamchoudhary6977 Рік тому

      I have the same doubt, like what is the need of locking again, when it will reach to top after while loop it will get locked automatically by the unique_lock, so why there is need of manual locking

    • @MaxRob27
      @MaxRob27 Рік тому

      @@sangamchoudhary6977 Becouse of the destructor of unique_lock. if the object ul goes out of scope the destructor call g_mutex.unlock(), and if it does it while g_mutex is already unlocked it will cause an error, so before end of {} lock is required to avoid the error

  • @tiantianliu5958
    @tiantianliu5958 Рік тому

    Thank you Arisaif. It is clear and help me to eliminate some confusion.

  • @kanhaglobal
    @kanhaglobal 3 роки тому

    You put lots of work and time to explain this kind of hard topics with clear Presentations and Code ... Its make a huge difference in the Understanding of MT. GREAT WORK !!! Thanks A LOT.

  • @Hugo_Musk
    @Hugo_Musk Рік тому

    I love you man! You save my life and school's project!! Thanks a lot!

  • @anujabellad1634
    @anujabellad1634 2 роки тому

    Superb! You explain it very neatly and nicely.

  • @minzhou1895
    @minzhou1895 3 роки тому +1

    such a clear video. Looking forward to the other ones!

    • @arisaif
      @arisaif  3 роки тому +1

      Happy to help :)
      Please consider subscribing to my channel.

  • @emadpres
    @emadpres 3 роки тому

    Another underated awesome video!

  • @bilvadala
    @bilvadala 3 роки тому +1

    One of the no nonsense and to the point video series I have ever got to watch and learn. Thank you so much, for your efforts.
    I have one suggestion on wider applicability of the language and taking advantage of object oriented programming through Design patterns.
    Would it be possible to start a new video series purely on Design patterns?

    • @arisaif
      @arisaif  3 роки тому

      Great idea! Thanks for the suggestion.

  • @seanngpack4987
    @seanngpack4987 4 роки тому +2

    awesome video! Very clear explanations, and visuals, feels like a solid college lecture

  • @shuaili5656
    @shuaili5656 2 роки тому

    holy crap, I finally understand the condition variable stuffs, thank un soo much !

  • @pythagorasaurusrex9853
    @pythagorasaurusrex9853 3 роки тому

    This tutorial is much appreciated! I enjoyed it and learned a lot about multi-threading stuff.

    • @arisaif
      @arisaif  3 роки тому

      Glad you found it helpful!

  • @cheggmi3637
    @cheggmi3637 3 роки тому

    Your gift is teaching. You make a complex topics so simple. Thank you for all your videos, they have changed my understanding of C++. I will appreciate it if you can come up with a C++ training video with practical examples and projects, obviously not for free 😂. That, Sir, will be wonderful.

  • @swarnendurc
    @swarnendurc 3 роки тому

    Excellent explanation. 🙏👌🏾

    • @arisaif
      @arisaif  3 роки тому +1

      Glad it was helpful! Please subscribe to my channel to get informed when the new content is available!

  • @bioaktywny
    @bioaktywny Рік тому

    Dear Ari,
    I learned a lot watching your videos. I like your way of explaining issues. I wonder whether you're going to record those videos on other multithreading issues, including Thread Pool, threading safety in STL and those Abstract Multithreading ideas.
    It would be really helpful to me and I guess also many of your subscribers (including me - your subscriber :-)

  • @juliusmazer6485
    @juliusmazer6485 4 роки тому

    Great video. Topics are both explained and presented very well. I wish you were my professor in college. Looking forward to everything multi threading!

  • @abeledea620
    @abeledea620 2 роки тому

    Thanks a lot for the info man,life savior video

  • @thestarinthesky_
    @thestarinthesky_ Рік тому

    Thanks for this great tutorial! I got a question here:
    @19:03 if we have multiple threads trying to read a shared resource but there is no modification, would we still need to have the critical section mutext locked?
    I mean race condition happens when at least one thread is trying to modify (write) into the shared resource. When there is no modification, what is the point of having mutex anyway as it will come at the price of performance, right?

    • @arisaif
      @arisaif  Рік тому +1

      You are correct, if there is no writer at all, no need for a critical section. However, notice that at 19:03, when I mention there is no writer, I mean that when there is no writer **at that moment** in the critical section. In that case multiple readers can be in the critical section. Later on, a writer might decide to write and get into the critical section. Once this happens, no reader can be there and the writer can write. In other words, the critical section provides mutual exclusivity between a set of readers and a single writer.

    • @thestarinthesky_
      @thestarinthesky_ Рік тому

      ​@@arisaif Thanks for the clarification. Please post more videos on C++ multithreading.

  • @kilianklankers4418
    @kilianklankers4418 3 роки тому

    Thank you for this awesome video! Keep Working :-)
    I got a question. At 32:26 you explain, that the sender lock on a shared mutex, and the reader lock on a unique mutex. At 18:46 you explain, that on the shared lock with shared mutex, multiple readers can be there, with no writer holding the lock.
    So I thought, that at 32:26, the sender must hold a unique lock, and the reciever can be a shared lock ( just reading data).
    Maybe you can help me to understand the my understanding problem?

  • @uchechinwasike1783
    @uchechinwasike1783 3 роки тому

    This makes it so clear. Thank you!

  • @avivran1198
    @avivran1198 3 роки тому

    Hi, on time 29:40.... I don;t understand the following 2 (C++ code) lines
    // if blocked, ul.unlock() is automatically called.
    // if unblocked, ul.lock() is automatically called.
    If mutex is taken at the time of call, the caller is blocked... so what do you mean by "if blocked, ul.unlock() is automatically called"
    how come "ul.unlock() is automatically called"??
    Thanks for the very good series. Appreciated!!

    • @arisaif
      @arisaif  3 роки тому

      // if blocked, ul.unlock() is automatically called:
      This means if the condition is not met (i.e., we have to block and wait), then ul.unlock() is automatically called so that other threads can progress and hopefully set the condition at some point so that we get unblocked.
      // if unblocked, ul.lock() is automatically called:
      This means if the condition is met and we can progress, then ul.lock() is automatically called so that other threads cannot get into the critical section.
      Hope this helps.
      If you find my channel helpful, please consider subscribing.Thanks!

  • @almosteasy9590
    @almosteasy9590 3 роки тому

    Awesome video bro.. Keep it up 👆

  • @umakantasenapati726
    @umakantasenapati726 4 роки тому

    Hi Arisaif....Excellent video on multi threading synchronization techniques ...Please come up with video on theadsfae STL ,CSP and Map Reduce...... Also i have watched your video on Atomic variables and memory model...It is very easy and clear to understand such a complex topic.....Keep making these type of videos....

  • @mrsinho1116
    @mrsinho1116 3 роки тому +2

    Very clear, thank you a lot

  • @eigenfield
    @eigenfield Рік тому

    Thank you. I have one question: Is mutual exclusion preserved during a "spurious wake-up"? To re-phrase it in another way, does the Consumer owns the mutex during a spurious wake-up?

  • @jingtaoren1493
    @jingtaoren1493 2 роки тому

    Wonderful job!!! Thanks~

  • @mitgharrib
    @mitgharrib 4 роки тому

    Excellent video. Thanks. I am looking forward for your videos on Atomics (memory models) hopefully soon.

    • @arisaif
      @arisaif  4 роки тому

      Thanks mitgharrib, glad you liked it!

  • @lopyus
    @lopyus 3 роки тому +1

    Very clear video. Thanks for publishing this! Can you give the slides too?

  • @kreaturen
    @kreaturen 2 роки тому

    I'm just wondering, at 14:45, the result appears almost instantaneously, but on my computer it takes quite some time actually... Did you skip it or is it supposed to run really quick?

    • @arisaif
      @arisaif  2 роки тому

      It doesn't take a long time on my system. Make sure you are compiling with -O3 and also in release mode.
      You could reduce the number of threads and the number of times we loop. See this example: godbolt.org/z/o7o6frP7x

    • @kreaturen
      @kreaturen 2 роки тому

      @@arisaif Thanks. I'm using Visual Studio atm (using a school licence), and unable to find reference to this -o3 thing, so I'm not exactly sure what that is or where to put it.
      It's not a big deal though, doesn't matter a bit slow. It's just for practice and your tutorials help a lot. Threads and shared resources proved a bit tricky to get right on my own, but this is on point 😅👍

  • @АндрейБорисов-и4о
    @АндрейБорисов-и4о 3 роки тому

    Thanks! Cool video lesson!

  • @87yassmin
    @87yassmin 3 роки тому

    Very clear Thanks 👌

  • @idoneoscom
    @idoneoscom 3 роки тому

    Excelent!! thankyou Ari!

  • @rajatmittal4673
    @rajatmittal4673 3 роки тому

    Awesome content well explained.

  • @autonomy_
    @autonomy_ 4 роки тому +1

    Great video, one question though. Why does the ready variable exist for conditional variables? Seems odd that you would notify with ready set to false.

    • @arisaif
      @arisaif  4 роки тому +1

      Because of spurious wake ups. If wait doesn't have a predicate (i.e. checking for ready), the thread might wake up without the producer actually sending a notification. From Wikipedia: "To allow for implementation flexibility in dealing with error conditions and races inside the operating system, condition variables may also be allowed to return from a wait even if not signaled, though it's not clear how many implementations actually do that."......... "Because spurious wakeups can happen whenever there's a race and possibly even in the absence of a race or a signal, when a thread wakes on a condition variable, it should always check that the condition it sought is satisfied. If it's not, it should go back to sleeping on the condition variable, waiting for another opportunity."
      en.wikipedia.org/wiki/Spurious_wakeup
      Also see this: stackoverflow.com/questions/8594591/why-does-pthread-cond-wait-have-spurious-wakeups

  • @praveenbs8146
    @praveenbs8146 3 роки тому

    Amazing video.

  • @haiangho8210
    @haiangho8210 4 роки тому +2

    Thank you arsaif, I have a question
    In the condition variable section. I wonder why do we have to put g_cv.notify_one() into ul.clock() and ul.unlock()? What if we remove ul.clock() and ul.unclock() what will happend? I tried on my PC and it still works if I remove. Is there any risk?

    • @arisaif
      @arisaif  4 роки тому +2

      I think you mean ul.lock(). There is some explanation at 32:29. Quoting from en.cppreference.com/w/cpp/thread/condition_variable/notify_one:
      "The notifying thread does not need to hold the lock on the same mutex as the one held by the waiting thread(s); in fact doing so is a pessimization, since the notified thread would immediately block again, waiting for the notifying thread to release the lock."
      In general, it is better to only keep only whatever is needed in the critical section. If you don't have to put something int he critical section (in this case notify_one), it is better to take it out.

    • @rbaccount4878
      @rbaccount4878 4 роки тому

      @@arisaif Thanks for the link and explanation. From your quoted text, I did not understand the part ".... since the notified thread would immediately block again, waiting for the notifying thread to release the lock". Please can you explain in detail. Thanks in advance.

  • @maddieb9087
    @maddieb9087 4 роки тому +2

    Fight on ✌️

  • @mdirshadalam2617
    @mdirshadalam2617 4 роки тому

    Hi Saif,
    Thanks for the video, it helps lot me.
    I made a thread to sleep for ten minutes using sleep_for. When other thread(s) comes , I need to wakeup , do the task and again sleep.
    Please help me for the issue.
    Thanks
    Irshad

  • @armaghanasghar2911
    @armaghanasghar2911 Рік тому

    Really good!

  • @alessioinfantino5793
    @alessioinfantino5793 2 роки тому

    I didn't understand the concept of predicate! More in particular, talking about the code, where is the predicate placed?

    • @arisaif
      @arisaif  2 роки тому +1

      It is placed on the wait on the conditional variable. The OS sometimes wakes threads up spuriously. Checking the predicate ensures that we pass the wait only when the notification has arrived.

  • @moviewatch12
    @moviewatch12 10 місяців тому

    How to wake up a specific thread?

  • @MrNyamchom
    @MrNyamchom 3 роки тому

    Very well done !

  • @amortalbeing
    @amortalbeing 4 роки тому

    Loved it Thanks a lot

  • @TheJank7
    @TheJank7 3 роки тому

    Those emojis are so funny, good humor :)

  • @angho8652
    @angho8652 4 роки тому

    Thank you, very useful

  • @danmarian3606
    @danmarian3606 4 роки тому

    perfect done

  • @frostfire2884
    @frostfire2884 Рік тому

    Hero

  • @momomadi2
    @momomadi2 2 роки тому

    thanks bro

  • @amortalbeing
    @amortalbeing 4 роки тому +1

    By the way what's the answer to your homework>?

  • @skilz8098
    @skilz8098 4 роки тому

    This in regards to the section of code that runs the Incrementer 1,000 times without locking the shared resources. I fully understand the concept of race conditions and how this code can generate undefined behavior. However, on my system (architecture, platform, and compiler) I can not reproduce these race conditions. I have an Intel Core 2 Quad Extreme running Windows 7 Ultimate x64 and I'm using Visual Studio 2017 with C++17 enabled. I've run this short program a dozen times, and every time I have run it, every printed value is 10,000. As I said, it should be undefined behavior, but I can't reproduce it. I don't know if this pertains to either Intel's CPU (ISA and instruction sets), the C++ compiler that I'm using, or Windows 7 task scheduler for multiple threads...
    However, I can take away quite a bit from this video when it comes to designing safe and reliable multithreading applications. Very good video with simple explanations and examples. Keep up the great work!!!

    • @arisaif
      @arisaif  4 роки тому +1

      I haven't tested on a system with specifications like yours, but I suspect it just happens to give you the right answer on your machine, but this won't be guaranteed if you move your code to another machine. I suggest you also try it on a different machine or perhaps an online compiler like this: www.onlinegdb.com/online_c++_compiler

  • @bharathkothandaraman5958
    @bharathkothandaraman5958 2 роки тому

    great

  • @ThePaypay88
    @ThePaypay88 3 роки тому

    great english