Reputation: 59
[C++ using Visual Studio Professional 2012]
Hi All, I am having trouble using std::mutex to prevent main() from changing variables that a second thread is accessing. In the following example (which is a massively simplified representation of my actual program) the function update() runs from the std::thread t2 in main(). update() checks if the vector world.m_grid[i][j].vec is empty and, if it is not, modifies the value contained. main() also accesses and occasionally clears this vector, and as a result if main() clears the vector after the empty check in update() but before world.m_grid[i][j].vec[0] is modified you get a vector subscript out of range error. I am trying to use std::mutex to prevent this from happening by locking barrier before the update() empty check, and releasing it after world.m_grid[i][j].vec[0] has been modified by update(), and after extensive browsing of mutex tutorials and examples I am unable to understand why the following does not have the desired effect:
#include <cstdlib>
#include <thread>
#include <mutex>
#include <vector>
using namespace std;
mutex barrier;
class World
{
public:
int m_rows;
int m_columns;
class Tile
{
public:
vector<int> vec;
int someVar;
};
vector<vector<Tile> > m_grid;
World (int rows = 100, int columns = 200): m_rows(rows), m_columns(columns), m_grid(rows, vector<Tile>(columns)) {}
};
void update(World& world)
{
while (true)
{
for (int i = 0; i < world.m_rows; ++i)
{
for (int j = 0; j < world.m_columns; ++j)
{
if (!world.m_grid[i][j].vec.empty())
{
lock_guard<mutex> guard(barrier);
world.m_grid[i][j].vec[0] += 5;
}
}
}
}
}
int main()
{
World world;
thread t2(update, ref(world));
while (true)
{
for (int i = 0; i < world.m_rows; ++i)
{
for (int j = 0; j < world.m_columns; ++j)
{
int random = rand() % 10;
if (world.m_grid[i][j].vec.empty() && random < 3) world.m_grid[i][j].vec.push_back(1);
else if (!world.m_grid[i][i].vec.empty() && random < 3) world.m_grid[i][j].vec.clear();
}
}
}
t2.join();
return 0;
}
I must be missing something fundamental here. Ideally the solution would just lock down world.m_grid[i][j] (leaving the rest of world.m_grid accessible to main()), which I assume would involve including a mutex in the class "Tile", but I run into the same problem as described here: Why does std::mutex create a C2248 when used in a struct with WIndows SOCKET? and have been unable to adapt the solution described to my project, so it would be extra helpful if someone was able to help me out there too.
-Thankyou for your time.
[edit] spelling
Upvotes: 2
Views: 935
Reputation: 6027
The problem you are having is that you are using so called clients' side synchronization. In other words: you have several threads and before every of them writes/reads shared resource, you have to use the barrier. As tune2fs has already replied, you have to lock_guard<mutex> guard(barrier)
before the call in main thread.
That said it would be much better for you to implement server side synchronization. In other words every block (if there are more then 1 lines - like in your main thread - you need to send all of them) would have to be synchronized by the server (World
).
Currently I could propose using a method void modify(Func<void(vector<int>&)> mutator);
in World
so you would send all your logic through this method as lambdas (the easiest). Inside modify
you would use the standard lock_guard
using a mutex
owned by World
. This solution is much more scaleable and safe (you really don't want to look at all you places where you invoke code that modifies the vector).
Upvotes: 1
Reputation: 7705
You need to lock the mutex also in you main function when you access the array:
...
for (int j = 0; j < world.m_columns; ++j) {
lock_guard<mutex> guard(barrier);
int random = rand() % 10;
if (world.m_grid[i][j].vec.empty() && random < 3) world.m_grid[i][j].vec.push_back(1);
else if (!world.m_grid[i][i].vec.empty() && random < 3) world.m_grid[i][j].vec.clear();
}
...
With mutexes you need to secure all accesses to your data. So far in your code thread 2 generates a mutex when it access the data. However the main thread can change the code as it does not know anything about the mutex. So the main thread can simply change the data.
Upvotes: 2