Skip to content

Latest commit

 

History

History
170 lines (119 loc) · 5.46 KB

06_recap.en.md

File metadata and controls

170 lines (119 loc) · 5.46 KB

Recap

Coders School

1. What was the most surprising for you? 😲

2. What was the most obvious for you? 🥱


Points to remember

  • Always use wait() with a predicate!
  • Remember about spurious wake-ups and lost notifications
  • Remember about a fight for locking a mutex when you use notify_all()
  • You can't choose which thread should wake up when you use notify_one()

Pre-test 🤯

// assume all necessary includes are here

int main() {
    std::mutex m;
    std::condition_variable cv;
    std::vector<int> v;
    std::vector<std::thread> producers;
    std::vector<std::thread> consumers;

    auto consume = [&] {
        std::unique_lock<std::mutex> ul(m);
        cv.wait(ul);
        std::cout << v.back();
        v.pop_back();
    };
    for (int i = 0; i < 10; i++) consumers.emplace_back(consume);

    auto produce = [&](int i) {
        {
            std::lock_guard<std::mutex> lg(m);
            v.push_back(i);
        }
        cv.notify_all();
    };
    for (int i = 0; i < 10; i++) producers.emplace_back(produce, i);

    for (auto && p : producers) p.join();
    for (auto && c : consumers) c.join();
}
  1. there may be an Undefined Behavior in this code
  2. the output is guaranteed to always be 0123456789
  3. v is always an empty vector at the end of this program
  4. if some producers threads started before some consumers, we could have a deadlock because of lost notifications
  5. this code can be improved by providing a predicate to wait() to disallow getting elements when the vector is empty
  6. a change from notify_all() to notify_one() + wait() with predicate will guarantee that each consumer thread will receive a different number

Note: 1, 4, 5, 6


Post-work


Homework: ping-pong

  • Thread A prints "ping" and the consecutive number
  • Thread B prints "pong" and the consecutive number
  • Ping always starts. Pong always ends.
  • Threads must work in turns. There may not be 2 consecutive pings or pongs. The program cannot end with a ping without a respective pong.
  • The program must be terminated either after the given number of repetitions or after the time limit, whichever occurs first. The reason for termination should be displayed on the screen.
  • Program parameters:
    • number of repetitions
    • time limit (in seconds)
$> g++ 03_ping_pong.cpp -lpthread
-std=c++17 -fsanitize=thread
$> ./a.out 1 10
ping 0
pong 0
Ping reached repetitions limit
Pong reached repetitions limit
$> ./a.out 12 1
ping 0
pong 0
ping 1
pong 1
ping 2
pong 2
Timeout

Tips

If you got stuck:

  • You need a mutex and a condition variable in your PingPong class
  • Wait for a condition variable with wait_for() in stop() function
  • Check the number of repetitions in ping and pong threads
  • Use an additional std::atomic variable which will tell all threads to end, when the required conditions are met
  • Ping and pong threads should be using wait() to check if it's their turn to work. Use an additional variable that will be used in the predicate passed to wait()
  • The pong thread should end the program after reaching the repetition limit

Repo on GitHub

https://github.com/coders-school/condition-variable

https://github.com/coders-school/condition-variable

  • Download a PDF
  • Browse presentation content
  • Find code examples
  • Do the homework!