简体   繁体   中英

Do I need to protect the read on a variable that is only modified in the same thread?

Let us consider two or more threads, and a resource. Should this be relevant, I am using C++11 on Ubuntu. The situation is illustrated by the following code:

#include <thread>
#include <mutex>

class Res
{
    //Data
};

void use_resource(const Res& rsc) {/*Do stuff*/}
void modify_resource(Res& rsc) {/*Modify the resource*/}

class A
{
    Res resource;
    std::mutex resource_mtx;
    std::thread thd;

    public:
    A()
    {
        thd = std::thread(&A::loop,this);      
    }

    void loop()
    {
        while(true)
        {
            use_resource(resource); //(Case 1)

            //Some work

            {
                std::lock_guard<std::mutex> mlock(resource_mtx);
                modify_resource(resource); //(Case 2)
            }
        }
    }

    Res get_resource()
    {
        std::lock_guard<std::mutex> mlock(resource_mtx);
        return resource; //(Case 3)
    }
};

int main()
{
    A a;


    while(true)
    {
        Res res1 = a.get_resource();

        //Do stuff with the resource
    }
}

We have a resource that contains some data. The function loop() in A runs on the first thread only. The other threads can call get_resource() to get access to the resource. The resource can only be modified by the function modified_resource .
My understanding is that there is a need for a lock in cases 2 and 3, since case 2 involves a write, and case 3 a read from another thread.

What I would like to know is if there is a need for a lock in case 1. From cppreference.com , the definition of a data race seems to be:

When an evaluation of an expression writes to a memory location and another evaluation reads or modifies the same memory location, the expressions are said to conflict. A program that has two conflicting evaluations has a data race unless:
i) both evaluations execute on the same thread or in the same signal handler, or
ii) both conflicting evaluations are atomic operations (see std::atomic), or
iii) one of the conflicting evaluations happens-before another (see std::memory_order)

I would think that there is no data race for case 1 in my code from those definitions:
There is no conflict between 1) and 2) (they are on the same thread, i) applies).
There is no conflict between 1) and 3) (none of them is a write).

This leads me to my two questions:
Q1) In the specific code provided, is there a need to protect the call to use_resource (Case 1) by a lock on resource_mtx to avoid a data race ?
Q2) Same question if this time cases 1 and 2 can be repeated, in any order or number, in the loop() function ? An arbitrary example would be:

while(true)
{
    {
        std::lock_guard<std::mutex> mlock(resource_mtx);
        modify_resource(resource); //(Case 2)
    }
    use_resource(resource); //(Case 1)      
    {
        std::lock_guard<std::mutex> mlock(resource_mtx);
        modify_resource(resource); //(Case 2)
    }
    use_resource(resource); //(Case 1)
    use_resource(resource); //(Case 1)
    {
        std::lock_guard<std::mutex> mlock(resource_mtx);
        modify_resource(resource); //(Case 2)
    }
}

As said above, my guess would be that there is no need for a lock in both cases, but I might very well be missing something, like compiler reordering or even the actual definition of a data race.

So far, I have only seen questions about read only access from a separate thread, which is my case 2, ( there and there ) or interactions between two different threads ( there ), but I have not seen this specific problem of read only access in the same thread.

EDIT: Edited the code to actually reflect a multithreaded scenario.

In your sample you are "safe" (assuming correct Res ).

Problematic cases happen when 2) (the write operation) would execute concurrently with 1) / 2) or 3) (without synchronization). 1) and 3) might happens concurrently, as read only.

1) and 2) are in the same thread, so cannot happen concurrently with 2) . 3) and 2) are protected by mutex .

Note that if in 3) , you don't create copy but return reference, then 3) mutex would be not extended correct scope.

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