samedi 29 avril 2017

Multiple threads access shared resources

I'm currently working on a particle system, which uses one thread in which the particles are first updated, then drawn. The particles are stored in a std::vector. I would like to move the update function to a separate thread to improve the systems performance. However this means that I encounter problems when the update thread and the draw thread are accessing the std::vector at the same time. My update function will change the values for the position, and colour of all particles, and also almost always resize the std::vector.

Single thread approach:

std::vector<Particle> particles;
void tick() //tick would be called from main update loop
{
    //slow as must wait for update to draw
    updateParticles();
    drawParticles();
}

Multithreaded:

std::vector<Particle> particles;
//quicker as no longer need to wait to draw and update
//crashes when both threads access the same data, or update resizes vector
void updateThread()
{
    updateParticles();
}
void drawThread()
{
    drawParticles();
}

To fix this problem I have investigated using std::mutex however in practice, with a large amount of particles, the constant locking of threads meant that performance didn't increase. I have also investigated std::atomic however, neither the particles nor std::vector are trivially copyable and so can't use this either.

Multithreaded using mutex:

NOTE: I am using SDL mutex, as far as I am aware, the principles are the same.

SDL_mutex mutex = SDL_CreateMutex();
SDL_cond canDraw = SDL_CreateCond();
SDL_cond canUpdate = SDL_CreateCond();
std::vector<Particle> particles;
//locking the threads leads to the same problems as before, 
//now each thread must wait for the other one
void updateThread()
{
    SDL_LockMutex(lock);
    while(!canUpdate)
    {
        SDL_CondWait(canUpdate, lock);
    }
    updateParticles();
    SDL_UnlockMutex(lock);
    SDL_CondSignal(canDraw);
}
void drawThread()
{
    SDL_LockMutex(lock);
    while(!canDraw)
    {
        SDL_CondWait(canDraw, lock);
    }
    drawParticles();
    SDL_UnlockMutex(lock);
    SDL_CondSignal(canUpdate);
}

I am wondering if there are any other ways to implement the multi threaded approach? Essentially preventing the same data from being accessed by both threads at the same time, without having to make each thread wait for the other. I have thought about making a local copy of the vector to draw from, but this seems like it would be inefficient, and may run into the same problems if the update thread changes the vector while it's being copied?

Aucun commentaire:

Enregistrer un commentaire