从一个对象线程中使用 const 方法是否安全

Is using a const method from one object thread safe?

本文关键字:const 方法 是否 安全 一个对象 线程      更新时间:2023-10-16

我不确定这是否是重复的,我已经浏览了许多帖子,但它们似乎与我的问题不够接近。

我想从一个对象使用 const 方法来同时更改其他对象。该程序基本上需要我在重力的影响下移动一个粒子,我想对所有粒子并行运行它。 我做了一个物理课,在该课中,我有一个 const 方法来移动粒子对象。

这里有一些示例类可以更好地理解我。

/**
 * Particle.h
 */
#ifndef __Particle__sim__
#define __Particle__sim__
class Particle {
  private:
    double height;
    double velocity;
  public:
    const double getHeight() const;
    const double getVelocity() const;
    void setHeight(const double&);
    void setVelocity(const double&);
};
#endif

/**
 * Physics.h
 */
#ifndef __physics__sim__
#define __physics__sim__
#include <thread>
#include <vector>
#include "Particle.h"
class Physics {
  private:
    double gravity;
    double timeStep;
    void moveParticle(Particle&, const double) const;
  public:
    Physics(const double g, const double t);
    void moveParticles(std::vector<Particle>&, const double) const;
};
#endif

/**
 * Physics.cpp
 */
#include "Physics.h"
using namespace std;   
Physics::Physics(const double g, const double t) : gravity(g), timeStep(t) {}
void Physics::moveParticle(Particle& particle, const double time) const {
  // move particle under gravity
}
void Physics::moveParticles(vector<Particles>& particles, const double time) const {
  vector<thread> threads;
  threads.reserve(particles.size());
  for (auto& p : particles) {
    threads.push_back(thread(Physics::moveParticle&, this, std::ref(p), time));
  }
  for (auto& t : threads) {
    t.join();
  }
}

这基本上是我的主要

/**
 * main.cpp
 */
#include <vector>
#include "Physics.h"
#include "Particle.h"
using namespace std;
int main() {
  vector<Particle> particles;
  // insert 100,000 particles
  Physics physics = Physics(-9.81, 0.01);
  physics.moveParticles(particles, 5.0);    
  return 0;
}

那么physics.moveParticle(Particle&, const double)线程在这里安全吗?

简短与甜蜜:我想使用一个 Physics 对象中的一种方法来制作多个线程来移动我程序中的所有粒子,但我不确定我编写的 const 方法是否线程安全。我不明白为什么不,但我无法证明它。

乍一看,这应该是线程安全的。

我们需要看到Particle::setHeight的实现才能绝对确定。如果它做了类似写入全局数组的事情怎么办?这将是愚蠢的,但我们不能确定。

但是,您的粒子看起来非常简单。更线程安全的是根本不改变它。使它们不可变,并在每次计算时创建一个新计算。

您仍可以通过将新粒子分配回旧粒子来更改它们。

但是,如果您真的想进入线程,那么这里一个很好的技术是拥有两种世界状态:上一个和下一个。这些与每个更新步骤交换。每个更新步骤从上一个步骤读取并写入下一个步骤。这允许其他线程(如图形)显示从以前的读取,而无需不断锁定像粒子这样的小东西。

这样,一个不可变的粒子根本不会减慢任何东西的速度。事实上,编译器会将机器代码重写为nextParticles[i] = updateParticle(prevParticles[i])直接赋值到其在内存中的最终位置。这就是RVO或NRVO。

对我来说,

它看起来是线程安全的。 特别是,如果每个生成的线程读取或写入的唯一(不可变)数据是线程自己的相应 Particle 对象,则就线程而言,没有共享数据。 (当然,Physics 对象本身是共享的,但由于您在子线程的生存期内没有修改 Physics 对象,因此 Physics 对象在操作期间实际上是不可变/只读的,并且生成的线程对 Physics 对象的任何只读访问都不会成为争用条件的来源)