来自线程函数内部的线程保护变量

Thread protected variables from inside a threaded function

我为从多线程函数生成像素的软件编写了一个简单的 raytracer 插件。 伪代码是

Scene* scene;

void engine_(int y, int, x, pixel& out){ // this function is threaded for each orizontal line y

Buffer* line;
my_render_engine(y, scene, buffered_line); // here is where I'm accessing a lot of shared data, incurring into data racing
out = *buffered_line; // here I have just a line of pixels
}

My Scene* 包含许多共享变量(相机、采样器、着色器等),渲染器只需要读取它们。这些是在调用 engine_ 之前生成的。同时访问它们会产生未定义的行为。

我已经获取了一个 thread_pool 模板,试图像这样保护我的线程

void engine\_(int y, int, x, pixel& out){ // this function is threaded for each orizontal line y

m.lock()
Buffer\* line(width);
auto render_line = \[this\](int y, int x, Buffer\* buffer) {
Scene thread_scene = Scene();
... creating scene ...

            }
    
            thread_world.camera_ptr->my_render_engine(y, x, r, thread_scene , buffer_image_ptr);
        };
    
        //for (int j = 0; j < height; j++)
        m_threadPool.AddTask(render_line, j, x, r, buffer_image_ptr); // running render function for each  
                                                                      //thread safely
    m.unlock()
    out = *buffered_image;  // here I have my entire image

}

这有点管用,或者至少我能够保护我的变量。它在很多方面都非常低效,需要改进,比如生成与我的内核数量恰到好处的场景。 最大的问题是,除非我等待生成整个图像,否则我无法想象我在做什么,这是我做不到的。 engine_ 函数让我在每一行像素生成后立即对其进行可视化,但我必须让它这样做。按原样使用我的线程池显然不起作用,因为我用简单的话覆盖了 engine_。

一个临时的解决方案是每次我 运行 这样的渲染器创建一个全新的场景

void engine_(int y, int, x, pixel& out){ // this function is threaded for each orizontal line y    
    Scene* thread_scene;    .....creating scene.....    Buffer* line(width);    
    my_render_engine(y, 3dscene, buffered_line); // here is where I'm accessing a lot of shared data, incurring into data racing    
    out =  *buffered_line; // here I have just a line of pixels 
}

它与 thread_pool 类似,但它让我可以逐行可视化生成的图像。 问题是没有很好的线程保护。在场景内部,有着色器和 3dsampler,它们在许多 3d 对象(如相机、灯光和几何体)之间共享。

所以这里是我需要帮助的地方:

我 'think' 当我创建我的场景时,其中的变量指针在线程之间共享,当线程同时访问它们时程序崩溃:

class Scene{
Camera* cam;
Sampler* sampler;
Object_list<object*> objs;
Tracer* tracer_ptr;
Shader_list<shader*> shaders
}; // these pointers refer to the same stuff

我的问题是: ->我说得对吗?即使我正在创建场景的多个副本,我也没有保护它们的变量? -> 如果是这样的话,我怎样才能使这些变量受到保护?希望我不需要复制它们(尤其是 mesh_ptr 非常重。

我会首先选择 std::shared_mutex 用作 read-write mutex

当您访问 reading 的共享变量时,您将使用 lock_sharedunlock_sharedtry_lock_shared 基元 - 并且,显然,您不会写入这些变量!

当您访问它们以 写入 时,您将使用通常的 lockunlocktry_lock 原语来获得独占访问- 然后,您就可以安全地编写它们了。

如果在那之后你的问题没有得到解决,那么你的代码中可能有其他损坏的东西。

我已经设法通过创建包含每个线程所需数据的映射和 thread::id 来解决问题。数据是 World class,其中包含共享 read-access 数据的指针和非共享数据的副本(希望是线程安全的,但我不完全确定)。

        const std::thread::id tID = std::this_thread::get_id();
        m_lock.lock();
        std::map<const std::thread::id, World*>::iterator thread_it = worlds_map.find(tID);


        if (thread_it == worlds_map.end())
        {
            //creating new scene for thread map
            World* thread_world = new World();
            thread_world->set_bg_texture(input_texture_ptr);
            thread_world->build();
            .........

            // adding to thread map
            worlds_map[tID] = thread_world;
        }

        thread_it = worlds_map.find(tID);

        m_lock.unlock();
        thread_it->second->camera_ptr->render_scene(y, x, r, buffer_image_ptr, *thread_it->second);
    }```

this way I only need to create an instance for each thread my cpu has and not for each row