Creating a lock that preserves the order of locking attempts in C++11

前端 未结 3 1942
南笙
南笙 2020-12-08 16:58

Is there a way to ensure that blocked threads get woken up in the same order as they got blocked? I read somewhere that this would be called a \"strong lock\" but I found no

3条回答
  •  旧时难觅i
    2020-12-08 17:11

    Are we asking the right questions on this thread??? And if so: are they answered correctly???

    Or put another way:

    Have I completely misunderstood stuff here??

    Edit Paragraph: It seems StatementOnOrder (see below) is false. See link1 (C++ threads etc. under Linux are ofen based on pthreads), and link2 (mentions current scheduling policy as the determining factor) -- Thanks to Cubbi from cppreference (ref). See also link, link, link, link. If the statement is false, then the method of pulling an atomic (!) ticket, as shown in the code below, is probably to be preferred!!

    Here goes...

    StatementOnOrder: "Multiple threads that run into a locked mutex and thus "go to sleep" in a particular order, will afterwards aquire ownership of the mutex and continue on in the same order."

    Question: Is StatementOnOrder true or false ???

    void myfunction() {
        std::lock_guard lock(mut);
    
        // do something
        // ...
        // mutex automatically unlocked when leaving funtion.
    }
    

    I'm asking this because all code examples on this page to date, seem to be either:

    a) a waste (if StatementOnOrder is true)

    or

    b) seriously wrong (if StatementOnOrder is false).

    So why do a say that they might be "seriously wrong", if StatementOnOrder is false?
    The reason is that all code examples think they're being super-smart by utilizing std::condition_variable, but are actually using locks before that, which will (if StatementOnOrder is false) mess up the order!!!
    Just search this page for std::unique_lock, to see the irony.

    So if StatementOnOrder is really false, you cannot run into a lock, and then handle tickets and condition_variables stuff after that. Instead, you'll have to do something like this: pull an atomic ticket before running into any lock!!!
    Why pull a ticket, before running into a lock? Because here we're assuming StatementOnOrder to be false, so any ordering has to be done before the "evil" lock.

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    std::mutex mut;
    std::atomic num_atomic{std::numeric_limits::max()};
    unsigned num_next{0};
    std::map mapp;
    
    void function() {
        unsigned next = ++num_atomic; // pull an atomic ticket
    
        decltype(mapp)::iterator it;
    
        std::unique_lock lock(mut);
        if (next != num_next) {
            auto it = mapp.emplace(std::piecewise_construct,
                                   std::forward_as_tuple(next),
                                   std::forward_as_tuple()).first;
            it->second.wait(lock);
            mapp.erase(it);
        }
    
    
    
        // THE FUNCTION'S INTENDED WORK IS NOW DONE
        // ...
        // ...
        // THE FUNCTION'S INDENDED WORK IS NOW FINISHED
    
        ++num_next;
    
        it = mapp.find(num_next); // this is not necessarily mapp.begin(), since wrap_around occurs on the unsigned                                                                          
        if (it != mapp.end()) {
            lock.unlock();
            it->second.notify_one();
        }
    }
    

    The above function guarantees that the order is executed according to the atomic ticket that is pulled. (Edit: using boost's intrusive map, an keeping condition_variable on the stack (as a local variable), would be a nice optimization, which can be used here, to reduce free-store usage!)

    But the main question is: Is StatementOnOrder true or false???
    (If it is true, then my code example above is a also waste, and we can just use a mutex and be done with it.)
    I wish somebody like Anthony Williams would check out this page... ;)

提交回复
热议问题