简体   繁体   中英

std::lock_guard seems to give thread safety despite scoped block

mutex m;
void thread_function()
{
    static int i = 0;

    for(int j =0; j<1000; j++)
    {
        { //this scope should make this ineffective according to my understanding
        lock_guard<mutex> lock(m);
        }
        i++;

       cout<<i<<endl;
    }
}

I call this function from 2 threads. Hence the expected value of int i is 1000*2 = 2000 if the function behaves in thread-safe fashion.

Without the mutex, the result varies from 1990 to 2000 as expected when printing i (due to non atomic int i). Inserting the lock guard without the scoped block prevents this.

However, by my understanding, having the scoped block around it should make it acquire and release the lock immediatly, hence there is no longer thread safety when writing to int i. However, I notice i to be 2000 always. Am I misunderstanding something?

Your understanding is correct and that the result is always 2000 is probably machine-dependent. The reason could be that the synchronization just before the i++ statement on your machine happens to always cause the threads to execute it with sufficient distance in time to avoid the race condition. However, as said, this is not guaranteed.

As others have already told you, the issue is that your i++ is so close to the thread synchronization that it (practically always) manages to do the update before the thread is preempted.

If we change your program like this:

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

std::mutex m;
void thread_function()
{
    static int i = 0;

    for (int j = 0; j < 1000; j++)
    {
        { //this scope should make this ineffective according to my understanding
            std::lock_guard<std::mutex> lock(m);
        }
        std::cout << "Printing some stuff. Weee!" << std::endl; // <- New line
        i++;

        std::cout << i << std::endl;
    }
}

int main()
{
    std::thread t1(thread_function);
    std::thread t2(thread_function);
    t1.join();
    t2.join();
    return 0;
}

Then sometimes the two threads will no longer sum to 2000. You will still hit the race condition less often than if the lock wasn't there, but this just shows a major danger with race conditions and undefined behavior: Your program can actually work most of the time , even if it is strictly wrong according to the standard.

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