Can someone help me understand wait() and condition_variables?

This is a discussion on Can someone help me understand wait() and condition_variables? within the C++ Programming forums, part of the General Programming Boards category; Hello All, I found this code online but it just doesn't seem to make any sense to me. Code: #include ...

  1. #1
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    801

    Can someone help me understand wait() and condition_variables?

    Hello All,

    I found this code online but it just doesn't seem to make any sense to me.

    Code:
    #include <iostream>
    #include <string>
    #include <thread>
    #include <mutex>
    #include <condition_variable>
     
    std::mutex m;
    std::condition_variable cv;
    std::string data;
    bool ready = false;
    bool processed = false;
     
    void worker_thread()
    {
        // Wait until main() sends data
        {
            std::unique_lock<std::mutex> lk(m);
            cv.wait(lk, []{return ready;});
        }
     
        std::cout << "Worker thread is processing data\n";
        data += " after processing";
     
        // Send data back to main()
        {
            std::lock_guard<std::mutex> lk(m);
            processed = true;
            std::cout << "Worker thread signals data processing completed\n";
        }
        cv.notify_one();
    }
     
    int main()
    {
        std::thread worker(worker_thread);
     
        data = "Example data";
        // send data to the worker thread
        {
            std::lock_guard<std::mutex> lk(m);
            ready = true;
            std::cout << "main() signals data ready for processing\n";
        }
        cv.notify_one();
     
        // wait for the worker
        {
            std::unique_lock<std::mutex> lk(m);
            cv.wait(lk, []{return processed;});
        }
        std::cout << "Back in main(), data = " << data << '\n';
     
        worker.join();
    }
    Okay, so let's talk about what I understand from this code.

    We're in the main-loop and we call a separate thread, passing to it the worker_thread function. Fair enough. Sticking to the main loop, we initialize a string and set it equal to "Example data". Fair enough.

    I just don't get how the data is sent between these threads.

    Mostly, this unique_lock and lock_guard thing. I guess I should do more reading but from the way the code is written, it seems like lock_guard needs to be called first and the same mutex shared between the threads needs to be passed as an argument. I guess that makes sense?

    I just don't get the use of wait() and why is there a lambda being passed to it? What purpose does returning a boolean do in the context of wait()? Does it have something to do with the lock_guard?

    And notify_one(), this is supposed to spuriously wake a thread, right?

    Gah, I feel so lost...

  2. #2
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,607
    unique_lock and lock_guard are RAII objects that acquire the lock in the constructor and release it in the destructor. unique_lock has additional features, but they aren't used in this example.

    To use condition variables correctly, there needs to be a "predicate loop" that checks "is the condition true". The worker thread's predicate is "ready", and the main thread's predicate is "processed".

    Predicate loops are needed for a couple of reasons. notify_one/notify_all will only wake threads that are actively blocked inside a wait. So calling notify_one while no threads are in a wait will do nothing.
    Code:
    cv.wait(lk, []{return ready;});
    This can be transformed into:
    Code:
    while (!ready)
        cv.wait(lk)
    So if we end up going into the wait (which releases the lock) then the notify_one will wake us up. If the notify_one was called before we acquired the lock (and entered the loop), then ready will be true and wait is never called.

    The other reason for predicate loops is spurious wake ups. That basically means that implementations of wait() are allowed to return even if notify_xxx wasn't called. Since it can return whenever it wants, you must have a predicate loop to ensure you're really done waiting.

    std::condition_variable::wait - cppreference.com

    gg

  3. #3
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    801
    Okay, now I have a serious design question.

    Let me describe what I'm attempting to multithread because now I'm starting to think a threadpool might be better for my needs than wait conditions and such. Though I guess maybe I can use this to implement my own threadpool. Oh God, that sounds like fun.

    So, I have a quadtree-esque structure. What I'm doing is akin to constructing a qaudtree to map a distribution of a set of 2D points.

    What I want to do is, take this point set and divide it up as well as I can between an arbitrary number of threads. So if there's 100 points, 4 processors get 25, 2 would get 50, 1 would get 100, etc.

    I then want to traverse the tree and gather up all relevant leaves. Ideally, each point finds its own unique leaf but sometimes that is not the case. So I want to analyze the leaf sets from each thread and compare, making sure that there are no identical elements and if there are then I need to establish an order for which points are inserted into the tree structure. This is all a rough cut of an algorithm.

    But the more I think about this algorithm, the more I think that I need a threadpool in lieu of wait conditions. Is there an implementation of a threadpool I can just download and use?

    Edit : Okay, I did it. I found that boost or whatever (which I've seen a lot of, actually) has a threadpool implementation, even available in the Arch Linux repositories so I got it all set-up and now I have a working threadpool. Thank God for that. Productivity, here I come!!!
    Last edited by MutantJohn; 11-05-2013 at 12:28 PM.

  4. #4
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,607
    >> the more I think that I need a threadpool in lieu of wait conditions.
    I don't follow - those two concepts aren't really related.

    >> So if there's 100 points, 4 processors get 25, 2 would get 50, 1 would get 100, etc ...
    Does each thread process the points into local quadtree? Or do they all compete for insertion into a single quadtree?

    gg

  5. #5
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    801
    It's all for a global quadtree. Each thread will be searching/modifying the same tree, hopefully in isolated areas.

    Also, I have the boost threadpool thing working but I'm not sure how to use it properly. Namely, I want it to be a global pool and I'm not sure how to bring control back to the main.

    In my main loop, I schedule tasks to the global pool but then I want control to return back to main() once the function returns. How do I do that? Or can I?

    Edit : Like right now, I have a big box. I just want to make sure my points are actually inside the box, using each thread's point set. So I just schedule the number of threads with binding the function to the appropriate arguments. After it does all the counting, I want to return back to main().
    Last edited by MutantJohn; 11-05-2013 at 02:07 PM.

  6. #6
    Registered User MutantJohn's Avatar
    Join Date
    Feb 2013
    Posts
    801
    Okay, I'm a moron. I totally am. Jesus.

    I realize that I must do this A LOT in the sense that I come crying to this forum in dire need of help but before anyone can actually reply (Lord forbid they have jobs), I figure out my bug.

    Turns out, when I was doing my global threadpool declaration, its size was 0.

    Why was that?

    Because num_threads was a global too and it was initalized to 0. I use command line input to reassign it but the global declaration had already allotted the size. So my threadpool's size was 0 the whole time.

    Now that I declare it inside my main, my code works perfectly with just using :
    Code:
    pool tp(num_threads);
    
    for (unsigned i=0; i < num_threads; ++i) 
       tp.schedule(boost::bind(/*...*/));
    
    tp.wait();
    And now my code seems to work perfectly well. Threadpooling, man. Threadpooling...

  7. #7
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,607
    >> Each thread will be searching/modifying the same tree, hopefully in isolated areas.
    Hope does not provide synchronization and determinism
    When 2 or more threads access the same memory location and at least one is modifying that location, then you must provide proper synchronization or it's wrong.

    >> I want control to return back to main() once the function returns. How do I do that? Or can I?
    Since they are all hitting the same tree, you could "join" with them and just wait until they are done.

    There is also the future/promise facilities.
    Or one could use a simple producer/consumer setup where main() is the consumer and the threads in the pool produce the results. These don't seem applicable though since you are all hitting a global tree.

    gg

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. wait
    By Wipe in forum C Programming
    Replies: 1
    Last Post: 01-18-2005, 02:45 PM
  2. how do u say like H (wait 1 second) I
    By Prodigy in forum C++ Programming
    Replies: 4
    Last Post: 05-06-2002, 05:05 AM
  3. Wait
    By drdroid in forum C++ Programming
    Replies: 8
    Last Post: 02-12-2002, 02:55 PM
  4. wait!
    By Unregistered in forum C++ Programming
    Replies: 1
    Last Post: 12-06-2001, 09:06 PM
  5. wait(1);
    By sp00k in forum C++ Programming
    Replies: 1
    Last Post: 10-25-2001, 07:09 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21