多线程光线追踪器

Multi threading Raytracer

我正在制作光线追踪器,我正在尝试使用 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], 0, (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, 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, y, *c);
            color = get_intersections(&ray, c);
            if (c->ligth)
                color = shading(&ray, color, c);
            my_mlx_pixel_put(&c->img, x, y, 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 的唯一参数对象的指针。因此从 t_file 中删除 thread 成员。它可能在那里没有任何用处。并创建一个 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], 0, (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_* 函数(return 值、中断等待等)的文档。