vector push_back creates error when multi threading

Started by
13 comments, last by SmkViper 8 years, 6 months ago

Or the better option would be for this doit function to be a member function instead, and the mutex is a member variable.
That way the caller can't pass in the wrong mutex.

(we're pretty far off-topic at this point, and I should probably create a new thread, but with apologies to the OP...)

You've now gone the classic OOP route, and encapsulated the mutex such that the caller can never know who else has access to it. This might be fine for an inner class, or a class which is used by a single consumer. For any shared object though, this is about as bad as the global mutex.

Data is important, people. Always know where your data is, who has access to it, what operations they are performing on it. Hiding it all away inside a shared object is like sticking your head in the sand.


I'm confused.

If you already are sharing the data, why aren't you bundling the mutex with it? I can't think of any legitimate use for keeping them separate since protecting the same memory location with two separate mutexes might as well not be locked at all.

Unless I've misread something entirely.
Advertisement


If you already are sharing the data, why aren't you bundling the mutex with it? I can't think of any legitimate use for keeping them separate since protecting the same memory location with two separate mutexes might as well not be locked at all.

If you bundle both the lock and the data together in one object, you've just said that "anyone with a reference to this object can access this at any time, but you can block indefinitely depending on the number of references".

That's perfectly fine if that is in fact your intent. But it's a very weird intent, and probably not what most people think of when they are trying to parallelise their system.

As you correctly surmise, the way out of this quandary is to stop sharing your data with the world. And what's the best way to enforce that? Quit sharing the mutex with the world.

if you encapsulate the mutex in the object, it's very easy to make a mistake and lock the mutex from unexpected places. If you have to explicitly pass both the object and the mutex, and the client has to explicitly lock the mutex... Suddenly you have a clear audit trail of who is taking locks and why.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

If you already are sharing the data, why aren't you bundling the mutex with it? I can't think of any legitimate use for keeping them separate since protecting the same memory location with two separate mutexes might as well not be locked at all.

If you bundle both the lock and the data together in one object, you've just said that "anyone with a reference to this object can access this at any time, but you can block indefinitely depending on the number of references".

That's perfectly fine if that is in fact your intent. But it's a very weird intent, and probably not what most people think of when they are trying to parallelise their system.

As you correctly surmise, the way out of this quandary is to stop sharing your data with the world. And what's the best way to enforce that? Quit sharing the mutex with the world.

if you encapsulate the mutex in the object, it's very easy to make a mistake and lock the mutex from unexpected places. If you have to explicitly pass both the object and the mutex, and the client has to explicitly lock the mutex... Suddenly you have a clear audit trail of who is taking locks and why.


I'm still not understanding you - though we're off-topic, so I'll take it to PMs


I'm still not understanding you - though we're off-topic, so I'll take it to PMs

I don't think this is too far OT, and/or at least someone coming across this some time later should understand it.

So (if even I understand it correctly) what he means is that since the function already operates on independant data, the mutex should not be shared. Meaning that you can do stuff like:


int functionA()
{
    std::vector<int> vSomeData;
    std::thread worker(doit(vSomeData)); 
}

int functionB()
{
    std::vector<int> vSomeOtherData;
    std::thread worker(doit(vSomeOtherData);
}

Now say you have the mutex static/global/class instance, whether. functionA and functionB might not know each other, they might lie in completely different parts of the program or in a different module for all that matters. In the case of a "shared" mutex, the thread of A and B could still block due to the other thread locking the shared mutex. Now if you do this:


int functionA()
{
    std::mutex someMutex;
    std::vector<int> vSomeData;
    std::thread worker(doit(vSomeData, someMutex)); 
}

int functionB()
{
    std::mutex otherMutex;
    std::vector<int> vSomeOtherData;
    std::thread worker(doit(vSomeOtherData, otherMutex);
}

A and B run entirely independantly of each other without blocking. You need to have a shared mutex if the function operates on shared data, but this is appearently better to be avoided. Please correct me if I'm wrong, I also heard of this from here first (though it makes sense to me).

A and B run entirely independantly of each other without blocking. You need to have a shared mutex if the function operates on shared data, but this is appearently better to be avoided. Please correct me if I'm wrong, I also heard of this from here first (though it makes sense to me).


Yeah, that's basically what I asked for clarification on.

Avoiding locking is great if you can get away with it, but I prefer to bundle locks with the shared data they lock rather than trying to make sure I passed the right parameter. (Assuming you can figure out the correct lock granularity)

This topic is closed to new replies.

Advertisement