多线程光线追踪器

问题描述

我正在制作一个光线追踪器,我正在尝试使用 pthread 来划分渲染。我注意到这对速度没有帮助,因为函数 pthread_join 很慢,如果我使用循环来使“await”更快并且几乎每次都能正常工作。但我不能使用它,因为渲染时间随场景而变化。 有没有办法以更有效的方式检查线程是否完成。这是代码。 `

int threats(t_file *c) //this function creates the threads
{
    int i;
    int err;

    pthread_t th[THREADS];
    i = 0;
    printf("1\n");
    c->thread = -1;
    mlx_clear_window(c->mlx_ptr,c->win_ptr);
    while (i < THREADS)
    {
        err = pthread_create(&th[i],(void *)paint_scene,(void *)c);
        if (err)
            return parse_error("Thread Error: CAN NOT CREATE THREAD");
        i++;
    }
    
    // while (i-- >= 0)
    //  pthread_join(th[i],0);

    //my await function xd
    while (i < 200000000)
        i++;
    mlx_put_image_to_window(c->mlx_ptr,c->win_ptr,c->img.mlx_img,0);

    c->thread = 0;
    return 1;
}

void paint_scene(void *a)

{

    int y;
    int x;
    t_ray ray;
    int color;
    t_file *c;

    c = (t_file *)a;
    color = 0;
    c->thread++;
    y = (c->thread * (c->win_heigth / THREADS));
    printf("y:%d,hilo%d\n",y,c->thread);
    while (y < (c->thread + 1) * (c->win_heigth / THREADS))
    {
        x = 0;
        while (x < c->win_width)
        {
            ray = generate_ray(x,*c);
            color = get_intersections(&ray,c);
            if (c->ligth)
                color = shading(&ray,color,c);
            my_mlx_pixel_put(&c->img,x,color);
            x++;
        }
        //ft_printf("\rLoading%d: %d%%",c->thread,y / (c->win_heigth / 100));
        y++;
    }
    pthread_exit(0);
}
`

解决方法

您的线程函数中存在并发问题:

c->thread++;
y = (c->thread * (c->win_heigth / THREADS));
printf("y:%d,hilo%d\n",y,c->thread);
while (y < (c->thread + 1) * (c->win_heigth / THREADS)) 
{
  ....
}

c->thread 在所有线程之间共享,并且基于可能的线程计时和当前的月球表面,我可以做出有根据的猜测并说第一个线程正在计算整个图像。启动时,第一个线程可能会看到 c->thread == -1,但稍后(如果线程启动比 while 循环快)其他线程增加该值,直到第一个线程看到 c->thread == THREADS-1

要解决这个问题,每次调用 create_thread 都必须传递一个指向唯一参数对象的指针,该对象包含该线程 ID。因此,从 thread 中删除 t_file 成员。它在那里可能没有任何意义。并创建一个 struct 类型来保存线程函数的参数:

struct thread_param
{
     unsigned int thread;
     file_t *c;
}

你在启动线程时像这样使用它:

struct thread_param params[THREADS];
while (i < THREADS)
{
    params[i].thread = i;
    params[i].c = c;
   
    err = pthread_create(&th[i],(void *)paint_scene,(void *)&(params[i]));
    if (err)
        return parse_error("Thread Error: CAN NOT CREATE THREAD");
    i++;
}

然后您访问线程函数中的数据:

void paint_scene(void *a)
{
  struct thread_param *param = (struct thread_param *)a;
  unsigned int thread = param->thread;
  t_file *c = param->c;

   /* 
     in the rest of the code you remove `c->thread++` 
     and replace `c->thread` with `thread`
   */
   ....
}
,

如果您有原子数据类型(C11,#ifndef __STDC_NO_ATOMICS__),那么实现一个全局计数器并等待它达到零(如果减少)或线程数量(如果增加)。

例如

#include <stdatomic.h>

atomic_int num_jobs;

void* thread_func(void*)
{
    //the work to do in the thread function
    //before exit decrease counter
    --num_jobs;
    pthread_exit(0);
}

int main()
{
    num_jobs = THREADS;      // same as your number of threads
    create_threads(THREADS); // number of threads = THREADS
    while (num_jobs) {       // loop while threads running
        //sleep for a while
    }
    join_threads();          // join threads for cleanup

    return 0;
}

其他经典的锁机制,

例如

#include <pthread.h>
    
pthread_spinlock_t lock;
int num_jobs;

// called by main
int numJobs()
{
    pthread_spin_lock(&lock);
    int res = num_jobs;
    pthread_spin_unlock(&lock);
    return res;
}

// called by thread_func
void decNumJobs()
{
    pthread_spin_lock(&lock);
    --num_jobs;
    pthread_spin_unlock(&lock);
}

int main()
{
    pthread_spin_init(&lock,PTHREAD_PROCESS_PRIVATE);
    // the other stuff as before
    pthread_spin_destroy(&lock);
    return 0;
}

另一种选择是使用 pthread_cond_waitpthread_cond_signal(主要是为了避免 while 循环中的睡眠,在接收到信号后继续而不是基于固定的时间量)。

例如

#include <pthread.h>

int num_jobs;
pthread_cond_t cond;
pthread_mutex_t lock;

void decNumJobs()
{
    pthread_mutex_lock(&lock);
    if (--num_jobs == 0)
        pthread_cond_signal(&cond);
    pthread_mutex_unlock(&lock);
}

void* thread_func(void*)
{
    //the work to do in the thread function
    //before exit decrease counter
    decNumJobs();
    pthread_exit(0);
}

int main()
{
    num_jobs = THREADS;

    pthread_cond_init(&cond,NULL);
    pthread_mutex_init(&lock,NULL);
    pthread_mutex_lock(&lock);

    create_threads(THREADS);
    pthread_cond_wait(&cond,&lock);
    pthread_mutex_unlock(&lock);
    join_threads();

    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&lock);

    return 0;
}

注意:为了简单起见,没有错误检查或处理。强烈建议阅读 pthread_* 函数(返回值、中断等待等)的文档。