多线程光线追踪器
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_wait
和 pthread_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 值、中断等待等)的文档。
我正在制作光线追踪器,我正在尝试使用 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_wait
和 pthread_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 值、中断等待等)的文档。