简体   繁体   中英

C++ scoped lock in loop blocks another thread

Simple example:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex lock_m;

void childTh() {
    while(true) {
        //std::this_thread::yield();
        //std::this_thread::sleep_for(std::chrono::milliseconds(1));
        std::unique_lock<std::mutex> lockChild(lock_m);

        std::cout << "childTh CPN1" << std::endl;

        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

int main(int, char**) {
    std::thread thr(childTh);

    std::this_thread::sleep_for(std::chrono::milliseconds(200));

    std::unique_lock<std::mutex> lockMain(lock_m);

    std::cout << "MainTh CPN1" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(10));
    return 0;
}

Main thread blocks on lockMain and never reach "MainTh CPN1". I expect that main thread should acquire lock_m when childTh reach end of iteration because lockChild is destroyed and lock_m is released. But this never happens. Can you please describe in details why main thread don't have time to acquire the lock before childTh lock it again ? With sleep_for main can reach "MainTh CPN1", but with yield not. I know that condition_variable can be used to notify and unblock another thread, but is it possible to use just scoped lock ? So it looks that it is risky to use scoped lock in different threads, even if it the same lock.

In childTh , lockChild doesn't release the mutex until the iteration ends. Right after that iteration ends, it starts the next one. This means you only have the time between the destruction of lockChild and then the initialization of lockChild in the next iteration. Since that happens as basically the next instruction, there basically isn't any time for lockMain to acquire a lock on the mutex. To save CPU cycles a typical lock acquire is going to yield for a short duration, which is not as short as single instruction, so there is basically no chance of lockMain being able to lock the mutex as it would have to be timed perfectly. If you change childTh to

void childTh() {
    while(true) {
        //std::this_thread::yield();
        //std::this_thread::sleep_for(std::chrono::milliseconds(1));
        {
            std::unique_lock<std::mutex> lockChild(lock_m);

            std::cout << "childTh CPN1" << std::endl;
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

now you have a 1 second delay between when the mutex is release by lockChild and when it reacquired in the next iteration, which then allows lockMain to acquire the mutex.


Also note that you are not calling join on thr at the end of main. Not doing so causes thr 's destructor to throw an exception which will cause your program to terminate imporperly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM