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!
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.
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.
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.
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).
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!
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! :)
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.
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
@@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
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.
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?
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.
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 :-)
Great video. Topics are both explained and presented very well. I wish you were my professor in college. Looking forward to everything multi threading!
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?
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.
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?
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!!
// 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!
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....
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?
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?
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
@@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 😅👍
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.
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
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?
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.
@@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.
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
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.
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!!!
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
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!
amazing video! very clear explanations of stl multithreading capabilities!
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.
Excellent video ! hats off for your explanation and your knowledge... Great Work!
Best video to understand memory model, thread synchronisation. So impressed.
Everything is crystal clear n organised. Keep up the good work man ✌🏻
I was having such a hard time understanding this, but your effective and comprehensive breakdown got through to me.
Hands Down. One of the best professors on the planet.
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?
Amazing video, very informative, explained everything in clearly. Thank you for this video :)
This is truly genius and so good! I'm really glad for discovering this channel
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.
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.
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).
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!
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! :)
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.
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
@@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
Thank you Arisaif. It is clear and help me to eliminate some confusion.
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.
I love you man! You save my life and school's project!! Thanks a lot!
Superb! You explain it very neatly and nicely.
such a clear video. Looking forward to the other ones!
Happy to help :)
Please consider subscribing to my channel.
Another underated awesome video!
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?
Great idea! Thanks for the suggestion.
awesome video! Very clear explanations, and visuals, feels like a solid college lecture
holy crap, I finally understand the condition variable stuffs, thank un soo much !
This tutorial is much appreciated! I enjoyed it and learned a lot about multi-threading stuff.
Glad you found it helpful!
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.
Excellent explanation. 🙏👌🏾
Glad it was helpful! Please subscribe to my channel to get informed when the new content is available!
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 :-)
Great video. Topics are both explained and presented very well. I wish you were my professor in college. Looking forward to everything multi threading!
Thanks a lot for the info man,life savior video
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?
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.
@@arisaif Thanks for the clarification. Please post more videos on C++ multithreading.
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?
This makes it so clear. Thank you!
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!!
// 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!
Awesome video bro.. Keep it up 👆
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....
Very clear, thank you a lot
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?
Wonderful job!!! Thanks~
Excellent video. Thanks. I am looking forward for your videos on Atomics (memory models) hopefully soon.
Thanks mitgharrib, glad you liked it!
Very clear video. Thanks for publishing this! Can you give the slides too?
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?
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
@@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 😅👍
Thanks! Cool video lesson!
Very clear Thanks 👌
Excelent!! thankyou Ari!
Awesome content well explained.
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.
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
Amazing video.
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?
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.
@@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.
Fight on ✌️
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
Really good!
I didn't understand the concept of predicate! More in particular, talking about the code, where is the predicate placed?
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.
How to wake up a specific thread?
Very well done !
Loved it Thanks a lot
Those emojis are so funny, good humor :)
Thank you, very useful
perfect done
Hero
thanks bro
By the way what's the answer to your homework>?
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!!!
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
great
great english