I'm coding a simple client/server multithread program. The client threads are created like
for (int i = 0; i < 4; i++)
{
clients.emplace_back(work_finished, updated[i], free_client_count,i);
client_threads.emplace_back([&]
{
clients[i].work();
});
}
And the initiation parameters are
std::atomic_bool work_finished;
work_finished.store(false);
std::atomic_bool updated[4];
updated[0].store(false);
updated[1].store(false);
updated[2].store(false);
updated[3].store(false);
atomic_int free_client_count;
free_client_count.store(0);
But these client threads wouldn't run as expected, always result in a invalid memory access during update[i].load()
. If I create another for loop to create the threads, there is no problem.
for (int i = 0; i < 4; i++)
{
clients.emplace_back(work_finished, updated[i], free_client_count,i);
}
for (int i = 0; i < 4; i++)
{
client_threads.emplace_back([&]
{
clients[i].work();
});
}
Then I add some log information to client constructor. It turns out that sometimes the client.work()
is called before the constructor finishes, which result in an invalid memory access of update_recieved.load()
because the update_recieved
is not initiated yet.
Here comes the question: why would work
be called before the client is constructed when they are in the same loop? And why it's right to break the client constructor and thread constructor. The complete code is below
using namespace std;
class client
{
public:
std::atomic_int& ready_thread_count;
std::atomic_bool& update_recieved;
std::atomic_bool& work_finished;
const int i;
void recieve()
{
try
{
while (!update_recieved.load())
{
//std::cout << "not recieved " << i << std::endl;
std::this_thread::yield();
}
std::cout << "recieved " << i << std::endl;
update_recieved.store(false);
}
catch (std::exception e)
{
std::cout << e.what() << std::endl;
}
}
void compute()
{
// do something
}
void send()
{
ready_thread_count++;
}
public:
client(std::atomic_bool& in_work_finished, std::atomic_bool& in_update_recieved, std::atomic_int& in_free_thread_count,int in_i)
:work_finished(in_work_finished), update_recieved(in_update_recieved), ready_thread_count(in_free_thread_count), i(in_i)
{
std::cout << "client created " << i << std::endl;
}
void work()
{
recieve();
while (!work_finished.load())
{
compute();
send();
recieve();
}
send();
}
};
class server
{
public:
std::atomic_int& ready_thread_count;
std::atomic_bool* update_recieved_vector;
std::atomic_bool& work_finished;
const std::chrono::microseconds wait_time;
const int client_number;
int iteration;
void send()
{
for (int i = 0; i < client_number; i++)
{
std::cout << "update client " << i << std::endl;
update_recieved_vector[i].store(true);
}
}
void compute()
{
std::this_thread::sleep_for(wait_time);
iteration++;
std::cout << iteration << std::endl;
if (iteration == 50)
{
work_finished.store(true);
}
}
void recieve()
{
while (ready_thread_count.load() != client_number)
{
std::this_thread::sleep_for(wait_time);
}
ready_thread_count.store(0);
}
public:
server(std::atomic_int& in_free_thread_count, std::atomic_bool* in_update_recieved_vector, std::atomic_bool& in_work_finished, int in_client_member, int in_wait_time)
:ready_thread_count(in_free_thread_count), update_recieved_vector(in_update_recieved_vector), work_finished(in_work_finished), client_number(in_client_member), wait_time(in_wait_time)
{
iteration = 0;
}
void work()
{
while (!work_finished.load())
{
send();
recieve();
compute();
}
send();
recieve();
}
};
int main()
{
std::atomic_bool work_finished;
work_finished.store(false);
std::atomic_bool updated[4];
updated[0].store(false);
updated[1].store(false);
updated[2].store(false);
updated[3].store(false);
atomic_int free_client_count;
free_client_count.store(0);
server local_server(free_client_count, updated, work_finished, 4, 20);
vector<client> clients;
vector<thread> client_threads;
thread server_thread(&server::work, &local_server);
for (int i = 0; i < 4; i++)
{
clients.emplace_back(work_finished, updated[i], free_client_count,i);
client_threads.emplace_back([&]
{
clients[i].work();
});
}
server_thread.join();
for (int i = 0; i < 4; i++)
{
client_threads[i].join();
}
std::cout << "finaly" << std::endl;
}
Aucun commentaire:
Enregistrer un commentaire