繁体   English   中英

多线程访问共享资源

[英]Multiple threads access shared resources

我目前正在研究一个粒子系统,该系统使用一个线程,首先对粒子进行更新,然后绘制。 粒子存储在std::vector 我想将更新功能移到单独的线程中以提高系统性能。 但是,这意味着当更新线程和绘图线程同时访问std::vector时遇到了问题。 我的更新函数更改所有粒子的位置和颜色的值,并且几乎还总是调整std::vector大小。

单线程方法:

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

多线程:

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();
}

为了解决这个问题,我研究了使用std::mutex的方法,但是在实践中,由于有大量的粒子,线程的不断锁定意味着性能没有提高。 我还研究了std::atomic但是粒子和std::vector都不是简单可复制的,因此也不能使用它。

使用互斥锁的多线程:

注意 :据我所知,我使用的是SDL互斥锁,原理是相同的。

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);
}

我想知道是否还有其他方法可以实现多线程方法? 本质上是防止两个线程同时访问相同的数据,而不必让每个线程都等待另一个线程。 我曾考虑过制作要从中绘制的矢量的本地副本,但这似乎效率不高,并且如果更新线程在复制矢量时更改了矢量,可能会遇到相同的问题?

我将使用更精细的锁定策略。 与其在vector中存储particle对象,不如存储指向其他对象的指针。

struct lockedParticle { particle* containedParticle; SDL_mutex lockingObject; };

updateParticles()我将尝试使用SDL_TryLockMutex()获取单个锁定对象-如果无法获得对互斥锁的控制,我会将指向该特定lockedParticle实例的指针添加到另一个向量,然后稍后重试以更新它们。

我将在drawParticles()遵循类似的策略。 这依赖于这样的事实,即绘制顺序对于粒子并不重要,通常是这种情况。

如果不关心数据一致性,则可以通过将vector封装在自定义类中并仅在单个读/写操作上设置互斥锁来避免阻塞整个vector,例如:

struct SharedVector
{
    // ...
    std::vector<Particle> vec;

    void push( const& Particle particle )
    {
       SDL_LockMutex(lock);
       vec.push_back(particle);
       SDL_UnlockMutex(lock);
    }
}
//...
SharedVector particles;

然后,当然,您需要修改updateParticles()drawParticles()以使用新类型,而不是std::vector

编辑:您可以避免通过在updateParticles()drawParticles()方法中使用互斥对象来创建新结构,例如

void updateParticles()
{
    //... get Particle particle object
    SDL_LockMutex(lock);
    particles.push_back(particle);
    SDL_UnlockMutex(lock);
}

对于drawParticles()也应执行相同的操作。

如果向量一直在变化,则可以使用两个向量。 drawParticles将具有其自己的副本,而updateParticles将写入另一个副本。 完成这两个函数后,将updateParticles使用的向量交换,复制或移动到drawParticles要使用的drawParticles updateParticles可以从drawParticles使用的相同向量中读取,以获取当前粒子的位置,因此您无需创建一个完整的新副本。)无需锁定。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM